c++矩阵运算库eigen

将从 http://eigen.tuxfamily.org/index.php?title=Main_Page#Download 下载下来的压缩包解压, 将其中的Eigen文件夹放入到开发的项目文件夹中, 在调用的时候只需要调用

#include "Eigen/Eigen” 或 #include "Eigen/Dense”, 其中不同的包叙述如下:

多种包

1. 矩阵的初始化

/*单个赋值法*/
int main()
{
  MatrixXd m(2,2);  //MatrixXd 代表 这个矩阵是double类型, X代表具体的行数都动态编译的
  m(0,0) = 3;
  m(1,0) = 2.5;
  m(0,1) = -1;
  m(1,1) = m(1,0) + m(0,1);
  std::cout << "Here is the matrix m:\n" << m << std::endl;
  VectorXd v(2);
  v(0) = 4;
  v(1) = v(0) - 1;
  std::cout << "Here is the vector v:\n" << v << std::endl;
  
  
  m.resize(4,1);      //MatrixXd还可以通过resize调整大小
}

/*统一赋值法*/
int main()
{
  VectorXd m(10); //定义一个向量
  m.setZero();
  m.setOnes();
  m.setConstant(value);+++
  m.setRandom();
  m.setLinSpaced(size, low, high);  //此处会将该向量大小改为size大小, 并令其在low到high范围内均匀取点, 即步长是(high-low)/size
  
  MatrixXd m(10,10);  //定义一个矩阵
  //下面的方法都会将m的大小改为rows, cols
  x.setZero(rows, cols);    
  x.setOnes(rows, cols);
  x.setConstant(rows, cols, value);
  x.setRandom(rows, cols);

  x.setIdentity();    //将m变为单位矩阵, 大小基于原矩阵大小
  x.setIdentity(rows, cols);       
}

/*基于Map方法, 将数组映射为向量, 需要注意的是其只支持一维数组或向量的初始化, 对于多维数组而言, 需要循环初始化*/

//连续映射
float data[] = {1,2,3,4};
Map v1(data);       // uses v1 as a Vector3f object
Map  v2(data,3);     // uses v2 as a ArrayXf object
Map m1(data);       // uses m1 as a Array22f object
Map m2(data,2,2);   // uses m2 as a MatrixXf object

//间隔映射
float data[] = {1,2,3,4,5,6,7,8,9};
//其中InnerStride和OuterStride指的是步长对象, 而其泛型指的是步长的数值
Map >  v1(data,3);                      // = [1,3,5], 此处的3代表v1是一个3*1维的行的向量
Map >   v2(data,3,InnerStride<>(3));     // = [1,4,7], 此处的第一个3指代的是v2的维度, 第二个3指代的是步长

//需要注意的是矩阵的维度不能超过按照步长最大可取的元素个数, 如果超过, 就会Undefined behavior
Map >  v3(data,7);   
/*
结果为:
1
3
5
7
9
-1.97697e-22
-1.18826e+29
*/


/*
在用步长初始化矩阵时, 用的是OuterStride而不是向量的InnerStride

下面这两行矩阵初始化结果都为:
1 4 7
2 5 8
*/
Map >  m2(data,2,3);                    // 此处的2和3指代的m2是一个2*3维的矩阵
Map >   m1(data,2,3,OuterStride<>(3));   // 也就是说步长即可在泛型中指定, 也可在初始化时传入一个步长对象


//对于多维矩阵, 我们可以通过
int row=10,col=5;
MatrixXf m(10,5);
float arr[]={1,2,3,4,5};
for(int i=0;i(arr,col);  
}
cout< > vec(5,vector(5,1));
MatrixXf m(5,5)
for(int i=0;i(arr,5); //不过此处需要注意的是不要对arr取sizeof, 因为此时sizeof失效了, 不再能得到真实的arr的大小了
}
cout<

2. 矩阵的切片

/*
矩阵的切片在eigen中称作block, 可以通过如下方式
*/

int main()
{
  Eigen::MatrixXf m(4,4);
  m <<  1, 2, 3, 4,
        5, 6, 7, 8,
        9,10,11,12,
       13,14,15,16;
  
    cout<(1,0)这种形式, 这种形式的泛型会被忽略, 然后编译器会报错, 即找不到block这个方法
  cout<< m.block<2,3>(1,0) <(起点所在行, 起点所在列)
  /*
  结果是:
      5  6  7
      9 10 11
  */
  
  
}

除却上面提到的外, 还有下面这些简易版的

快速矩阵初始化

topRows和bottomRows函数对于Vector也是可行的, 除此之外, 对于Vector, 有如下:

Vector版的快速矩阵初始化

3. 实现矩阵形式的最小二乘法

最小二乘法的矩阵形式为:

那么第一步需要做的是对A进行QR分解, 即 , 其中Q是正交矩阵, 即 , 而R是右上三角矩阵, 即假如A是mn维, 则Q是 mm 维, R是 m*n 维, 只不过R只有右上角有值。即如下图所示。

QR分解

QR分解公式如下, 注意因为Q是正交矩阵所以 :
x = (A ^ { \top } A)^{-1}A ^ { \top } b \Rightarrow A ^ { \top } A x = A ^ { \top } b \Rightarrow R ^ { \top } R x = R ^ { \top } Q ^ { \top } b \Rightarrow R x = Q ^ { \top } b

那么现在的问题就变成了 , 那么我们可以更进一步, 将R进行LU分解, 也就是常见的高斯消去法, 在matlab中, 通过左除的形式, 即 R \ (Q'b) 这行代码。 在python中, 通过 np.linalg.solve()函数。

LU分解如下图所示。 即分解为为L(下三角矩阵), 和U(上三角矩阵), 注意L中对角线上都是1。

LU分解

如果对于行数不等于列数的, 其LU分解为:

\left[\begin{array} { r r r } { 0.68 } & { - 0.605 } & { - 0.0452 } \\ { - 0.211 } & { - 0.33 } & { 0.258 } \\ { 0.566 } & { 0.536 } & { - 0.27 } \\ { 0.597 } & { - 0.444 } & { 0.0268 } \\ { 0.823 } & { 0.108 } & { 0.904 } \end{array}\right]= \left[ \begin{array} { r r r r r } { 1 } & { 0 } & { 0 } & { 0 } & { 0 } \\ { - 0.299 } & { 1 } & { 0 } & { 0 } & { 0 } \\ { - 0.05 } & { 0.888 } & { 1 } & { 0 } & { 0 } \\ { 0.0296 } & { 0.705 } & { 0.768 } & { 1 } & { 0 } \\ { 0.285 } & { - 0.549 } & { 0.0436 } & { 0 } & { 1 } \end{array} \right]\left[\begin{array} { c c c } { 0.904 } & { 0.823 } & { 0.108 } \\ { 0 } & { 0.812 } & { 0.569 } \\ { 0 } & { 0 } & { - 1.1 } \\ { 0 } & { 0 } & { 0 } \\ { 0 } & { 0 } & { 0 } \end{array}\right]

因此此时能得到L和U矩阵, 那么我们可以将R替换为 , 那么对于 , 我们就有:

令 , 因此我们就可以先对 求解, 解出 的值, 接着对 求解, 即可得到 的值。

不过需要注意的是, 一般而言, 对于非奇异矩阵(秩不是满秩), 譬如, 是无法通过LU分解, 即高斯消去法来求得L和U矩阵的, 详细的说, 假设矩阵 为(其中为k*k矩阵):

仅当矩阵 满足如下定义时, 才可被LU分解:
\operatorname { Rank } \left( A _ { 11 } \right) + k \geq \operatorname { Rank } \left( \left[ \begin{array} { l l } { A _ { 11 } } & { A _ { 12 } } \end{array} \right] \right) + \operatorname { Rank } \left( \left[ \begin{array} { c } { A _ { 11 } } \\ { A _ { 21 } } \end{array} \right] \right)

所以说对于那些 不可求逆的最小二乘法, 即 不是满秩的情况, 通过QR分解和LU分解的结合也是无法求出正确的解的。

对于这种情况, 我们只能通过梯度下降法来近似找到局部最优解。 如果是矩阵形式的话, 可以通过 solve 函数, 无论是matlab中的solve函数, 还是python中的np.linalg.solve(), 还是c++ eigen库中的solve函数, 近似求解。

下面是eigen库通过先转换为QR分解再转换为LU分解的求解过程, 其中LU参考官方API

#include "Eigen/Eigen"
VectorXf main(const MatrixXf &A,const VectorXf &b){
    VectorXf x(b.rows());
    
    HouseholderQR QR=HouseholderQR(A);  //基于A矩阵创建QR对象
    MatrixXf Q=QR.householderQ(); //得到Q矩阵
    
    //因为A=QR, 而Q矩阵是正交矩阵, 所以R=Q'A, 然后用R矩阵构建LU对象
    FullPivLU LU(Q.transpose()*b);  //此时得到的LU矩阵对象相当于是L+U的混合, 需要将L和U从中分离开来
    

    //注意之前提到的对于非方阵矩阵的LU分解, 其中L矩阵的对角线上都是1, 所以可以认定为是一个单位矩阵和一个下三角矩阵的和
    MatrixXf L=MatrixXf::Identity(LU.matrixLU().rows(),LU.matrixLU().rows()); //设置一个单位矩阵
    L.block(0,0,LU.matrixLU().rows(),LU.matrixLU().cols()).triangularView()=LU.matrixLU();  //加上下三角矩阵
    
    
    MatrixXf U=LU.matrixLU().triangularView(); //而矩阵U对角线上不是1, 所以只需要取LU对象的上三角矩阵即可

    //此处可以输出看看L和U的维度
    cout<

上述解法只是其中一种解法, 无论是QR分解还是LU分解, eigen库中都有很多种不同的形式, 具体请参考eigen库的API, 归纳来说, 其总共提供了如下这几种矩阵分解方式。

矩阵分解方式

你可能感兴趣的:(c++矩阵运算库eigen)