当前位置:   article > 正文

Eigen学习笔记20:线性代数及其分解_selfadjointeigensolver

selfadjointeigensolver

基本的线性求解问题:

你有一个方程组,你已经写成一个矩阵方程Ax=b 其中Ab是矩阵(特殊情况下b可以是向量)。

您想找到一个解x

解决方案:You can choose between various decompositions,  取决于你的矩阵是什么一个样子,取决于你选择速度或准确性。

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix3f A;
  8. Vector3f b;
  9. A << 1,2,3, 4,5,6, 7,8,10;
  10. b << 3, 3, 4;
  11. cout << "Here is the matrix A:\n" << A << endl;
  12. cout << "Here is the vector b:\n" << b << endl;
  13. Vector3f x = A.colPivHouseholderQr().solve(b);
  14. cout << "The solution is:\n" << x << endl;
  15. }

输出:

  1. Here is the matrix A:
  2. 1 2 3
  3. 4 5 6
  4. 7 8 10
  5. Here is the vector b:
  6. 3
  7. 3
  8. 4
  9. The solution is:
  10. -2
  11. 0.999997
  12. 1

colPivHouseholderQr()方法返回类ColPivHouseholderQR的对象。

由于此处的矩阵类型为Matrix3f,因此该行可能已替换为:

  1. ColPivHouseholderQR<Matrix3f> dec(A);
  2. Vector3f x = dec.solve(b);
在这里,ColPivHouseholderQR是具有列旋转功能的QR分解。
这是本教程的一个不错的折衷方案,因为它适用于所有矩阵,而且速度非常快。这是一些其他分解表,您可以根据矩阵和要进行的权衡选择:
分解方法
矩阵要求
    速度
(中小)
 速度
(大)
准确性
PartialPivLUpartialPivLu()可逆的+++++
FullPivLUfullPivLu()没有----+++
HouseholderQRHouseholderQr()没有+++++
ColPivHouseholderQRcolPivHouseholderQr()没有+--+++
FullPivHouseholderQRfullPivHouseholderQr()没有----+++
CompleteOrthogonalDecompositioncompleteOrthogonalDecomposition()没有+--+++
LLTllt()正定+++++++
LDLTldlt()正或负
半定
++++++
BDCSVDbdcSvd()没有----+++
JacobiSVDjacobiSvd()没有-----+++

To get an overview of the true relative speed of the different decompositions, check this benchmark .

所有这些分解都提供了一个solve()方法,该方法与上述示例中的方法一样。

例如,如果您的矩阵是正定的,则​​上表说明LLTLDLT分解是一个很好的选择。

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix2f A, b;
  8. A << 2, -1, -1, 3;
  9. b << 1, 2, 3, 1;
  10. cout << "Here is the matrix A:\n" << A << endl;
  11. cout << "Here is the right hand side b:\n" << b << endl;
  12. Matrix2f x = A.ldlt().solve(b);
  13. cout << "The solution is:\n" << x << endl;
  14. }

输出:

  1. Here is the matrix A:
  2. 2 -1
  3. -1 3
  4. Here is the right hand side b:
  5. 1 2
  6. 3 1
  7. The solution is:
  8. 1.2 1.4
  9. 1.4 0.8

For a much more complete table comparing all decompositions supported by Eigen (notice that Eigen supports many other decompositions), see our special page on this topic.

检查解决方案是否确实存在

Only you know what error margin you want to allow for a solution to be considered valid.

因此,Eigen允许您自己进行此计算,如以下示例所示:

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix2f A;
  8. A << 1, 2, 2, 3;
  9. cout << "Here is the matrix A:\n" << A << endl;
  10. SelfAdjointEigenSolver<Matrix2f> eigensolver(A);
  11. if (eigensolver.info() != Success) abort();
  12. cout << "The eigenvalues of A are:\n" << eigensolver.eigenvalues() << endl;
  13. cout << "Here's a matrix whose columns are eigenvectors of A \n"
  14. << "corresponding to these eigenvalues:\n"
  15. << eigensolver.eigenvectors() << endl;
  16. }

输出:

  1. Here is the matrix A:
  2. 1 2
  3. 2 3
  4. The eigenvalues of A are:
  5. -0.236068
  6. 4.23607
  7. Here's a matrix whose columns are eigenvectors of A
  8. corresponding to these eigenvalues:
  9. -0.850651 -0.525731
  10. 0.525731 -0.850651

计算特征值和特征向量

You need an eigendecomposition here, see available such decompositions on this page

确保检查矩阵是否是self-adjoint,,就像在这些问题中经常发生的那样。

这是一个使用SelfAdjointEigenSolver的示例,可以使用EigenSolverComplexEigenSolver轻松地将其应用于一般矩阵。

特征值和特征向量的计算不一定会收敛,但是这种收敛失败的情况非常罕见。调用info()就是为了检查这种可能性。

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix2f A;
  8. A << 1, 2, 2, 3;
  9. cout << "Here is the matrix A:\n" << A << endl;
  10. SelfAdjointEigenSolver<Matrix2f> eigensolver(A);
  11. if (eigensolver.info() != Success) abort();
  12. cout << "The eigenvalues of A are:\n" << eigensolver.eigenvalues() << endl;
  13. cout << "Here's a matrix whose columns are eigenvectors of A \n"
  14. << "corresponding to these eigenvalues:\n"
  15. << eigensolver.eigenvectors() << endl;
  16. }

输出:

  1. Here is the matrix A:
  2. 1 2
  3. 2 3
  4. The eigenvalues of A are:
  5. -0.236068
  6. 4.23607
  7. Here's a matrix whose columns are eigenvectors of A
  8. corresponding to these eigenvalues:
  9. -0.850651 -0.525731
  10. 0.525731 -0.850651

计算逆和行列式

首先,请确保您确实想要这个。

尽管逆和行列式是基本的数学概念,但在数值线性代数中,它们不如在纯数学中流行。

Inverse运算通常可以用solve()操作代替,而行列式通常不是检查矩阵是否可逆的好方法。

但是,对于非常 小的矩阵,上述条件是不正确的,并且逆和行列式可能非常有用。

尽管某些分解(例如PartialPivLUFullPivLU)提供了inverse()和determinant()方法,但您也可以直接在矩阵上调用inverse()和determinant()。

如果矩阵的固定大小很小(最多为4x4),这将使Eigen避免执行LU分解,而应使用对此类小矩阵更有效的公式。

这是一个例子:

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix3f A;
  8. A << 1, 2, 1,
  9. 2, 1, 0,
  10. -1, 1, 2;
  11. cout << "Here is the matrix A:\n" << A << endl;
  12. cout << "The determinant of A is " << A.determinant() << endl;
  13. cout << "The inverse of A is:\n" << A.inverse() << endl;
  14. }

输出:

  1. Here is the matrix A:
  2. 1 2 1
  3. 2 1 0
  4. -1 1 2
  5. The determinant of A is -3
  6. The inverse of A is:
  7. -0.666667 1 0.333333
  8. 1.33333 -1 -0.666667
  9. -1 1 1

最小二乘求解

最小二乘求解的最准确方法是SVD分解。

Eigen提供了两种实现。推荐的对象是BDCSVD类,它可以很好地解决较大的问题,并自动退回到JacobiSVD类以解决较小的问题。对于这两个类,它们的resolve()方法都在进行最小二乘求解。

这是一个例子:

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. MatrixXf A = MatrixXf::Random(3, 2);
  8. cout << "Here is the matrix A:\n" << A << endl;
  9. VectorXf b = VectorXf::Random(3);
  10. cout << "Here is the right hand side b:\n" << b << endl;
  11. cout << "The least-squares solution is:\n"
  12. << A.bdcSvd(ComputeThinU | ComputeThinV).solve(b) << endl;
  13. }

输出:

  1. Here is the matrix A:
  2. 1 2 1
  3. 2 1 0
  4. -1 1 2
  5. The determinant of A is -3
  6. The inverse of A is:
  7. -0.666667 1 0.333333
  8. 1.33333 -1 -0.666667
  9. -1 1 1

可能更快但可靠性较低的另一种方法是使用法线矩阵的Cholesky分解或QR分解。decomposition. Our page on least squares solving has more details.

将计算与构造分开

在以上示例中,在构造分解对象的同时计算了分解。但是,在某些情况下,您可能希望将这两件事分开,例如,如果在构造时不知道要分解的矩阵,则可能会需要将它们分开。或者如果您想重用现有的分解对象。

使之成为可能的原因是:

  • 所有分解都有默认的构造函数,
  • 所有分解都具有执行计算的compute(matrix)方法,并且可以在已计算的分解中再次调用该方法,以将其重新初始化。

例如:

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix2f A, b;
  8. LLT<Matrix2f> llt;
  9. A << 2, -1, -1, 3;
  10. b << 1, 2, 3, 1;
  11. cout << "Here is the matrix A:\n" << A << endl;
  12. cout << "Here is the right hand side b:\n" << b << endl;
  13. cout << "Computing LLT decomposition..." << endl;
  14. llt.compute(A);
  15. cout << "The solution is:\n" << llt.solve(b) << endl;
  16. A(1,1)++;
  17. cout << "The matrix A is now:\n" << A << endl;
  18. cout << "Computing LLT decomposition..." << endl;
  19. llt.compute(A);
  20. cout << "The solution is now:\n" << llt.solve(b) << endl;
  21. }

输出:

  1. Here is the matrix A:
  2. 2 -1
  3. -1 3
  4. Here is the right hand side b:
  5. 1 2
  6. 3 1
  7. Computing LLT decomposition...
  8. The solution is:
  9. 1.2 1.4
  10. 1.4 0.8
  11. The matrix A is now:
  12. 2 -1
  13. -1 4
  14. Computing LLT decomposition...
  15. The solution is now:
  16. 1 1.28571
  17. 1 0.571429

最后,您可以告诉分解构造函数预先分配存储空间以分解给定大小的矩阵,以便在随后分解此类矩阵时,不执行动态内存分配(当然,如果您使用的是固定大小的矩阵,则不存在动态内存分配发生)。只需将大小传递给分解构造函数即可完成,如下例所示:

  1. HouseholderQR<MatrixXf> qr(50,50);
  2. MatrixXf A = MatrixXf::Random(50,50);
  3. qr.compute(A); // no dynamic memory allocation

Rank-revealing 分解

某些分解是Rank-revealing ,即能够计算矩阵的等级。这些通常也是在非满秩矩阵(在方形情况下表示奇异矩阵)的情况下表现最佳的分解。On this table you can see for all our decompositions whether they are rank-revealing or not.

Rank-revealing decompositions offer at least a rank() method. 

它们还可以提供方便的方法,例如isInvertible(),并且还提供一些方法来计算矩阵的内核(空空间)和图像(列空间),就像FullPivLU那样

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix3f A;
  8. A << 1, 2, 5,
  9. 2, 1, 4,
  10. 3, 0, 3;
  11. cout << "Here is the matrix A:\n" << A << endl;
  12. FullPivLU<Matrix3f> lu_decomp(A);
  13. cout << "The rank of A is " << lu_decomp.rank() << endl;
  14. cout << "Here is a matrix whose columns form a basis of the null-space of A:\n"
  15. << lu_decomp.kernel() << endl;
  16. cout << "Here is a matrix whose columns form a basis of the column-space of A:\n"
  17. << lu_decomp.image(A) << endl; // yes, have to pass the original A
  18. }

输出:

  1. Here is the matrix A:
  2. 1 2 5
  3. 2 1 4
  4. 3 0 3
  5. The rank of A is 2
  6. Here is a matrix whose columns form a basis of the null-space of A:
  7. 0.5
  8. 1
  9. -0.5
  10. Here is a matrix whose columns form a basis of the column-space of A:
  11. 5 1
  12. 4 2
  13. 3 3

当然,任何秩计算都取决于对任意阈值的选择,因为实际上没有浮点矩阵恰好是秩不足的。Eigen选择一个明智的默认阈值,该阈值取决于分解,但通常是对角线大小乘以机器ε。虽然这是我们可以选择的最佳默认值,但只有您知道您的应用程序的正确阈值是多少。您可以通过在调用rank()或需要使用此阈值的任何其他方法之前,在分解对象上调用setThreshold()来进行设置。分解本身(即compute()方法)与阈值无关。更改阈值后,无需重新计算分解。

  1. #include <iostream>
  2. #include <Eigen/Dense>
  3. using namespace std;
  4. using namespace Eigen;
  5. int main()
  6. {
  7. Matrix2d A;
  8. A << 2, 1,
  9. 2, 0.9999999999;
  10. FullPivLU<Matrix2d> lu(A);
  11. cout << "By default, the rank of A is found to be " << lu.rank() << endl;
  12. lu.setThreshold(1e-5);
  13. cout << "With threshold 1e-5, the rank of A is found to be " << lu.rank() << endl;
  14. }

输出:

  1. By default, the rank of A is found to be 2
  2. With threshold 1e-5, the rank of A is found to be 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/78189
推荐阅读
相关标签
  

闽ICP备14008679号