Eigen库入门:矩阵运算

一、预备工作

1.前言

博主最近在学习高博的《视觉SLAM十四讲》,将对其中一些常用的库文件的使用方式进行记录。本篇博文将简单记录如何使用Eigen库进行基础的矩阵运算。

博主的开发环境为Ubuntu20.04+Clion。

2.安装Eigen3库

一般而言,在Ubuntu安装完成时,将自动安装Eigen3库文件,但是为了防止存在误删或其他原因导致不存在库文件,此处建议大家运行如下指令进行安装库文件

sudo apt-get install -y libeigen3-dev

安装的默认位置一般为:/usr/include/eigen3,可以使用如下指令查看库文件的具体安装位置。

sudo update
locate eigen3

3.新建工程文件

打开Clion,新建工程study_eigen。在该工程下编辑新建的CMakeLists.txt文件,添加如下语句:

include_directories("/usr/include/eigen3")

4.引用头文件

打开main.cpp文件,输入如下内容:

#include
using namespace std;

// 导入Eigen库的核心和用于稠密矩阵运算的Dense
#include 
#inclued 
using namespace Eigen;

int main(int argc, chr **argv){
     
    
    return 0;
}

此部分内容引用了Eigen库的核心Core和用于稠密矩阵线性计算的Dense

一、矩阵的定义

1.定义矩阵

在Eigen库中定义矩阵主要采用如下语句:

Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>

其中三个参数分别为数据类型、行数、列数。例如定义一个三行两列的double类型矩阵的语句如下:

Matrix<double, 3, 1> matrix_32d

其中matrix_32d为定义的矩阵的名称,博主习惯用matrix表示该变量为一个矩阵,而32d代表该矩阵为三行两列的double类型矩阵。

2.定义向量

列向量即为N行1列的矩阵,同样行向量为1行N列的矩阵,在Eigen库中的定义方式如下:

// 行向量
Matrix<typename Scalar, int RowsAtCompileTime, 1>
Matrix<typename Scalar, 1, int ColsAtCompileTime>

为了使得向量的定义更加直观,Eigen库使用typedef关键字设置了VectorRowVector用于定义行向量和列向量。

// 定义一个3维float类型列向量
Vector3f vector_31f
// 定义一个2维int类型行向量
RowVector2i vector_12i

同定义矩阵一样,博主习惯将向量定义为vector,其后加上向量的维度和数据类型。

3.常用TypeDef

除了上述使用的定义方式外,Eigen库自定义了大量typedef用于快捷定义矩阵。

// 定义一个3X3的double类型矩阵
Matrix3d matrix_33d
// 定义一个3X3的float类型矩阵
Matrix3f matrix_33f

如果在开发过程中需要定义一个动态的矩阵或则向量可以使用如下方式进行定义

// 定义一个动态大小的矩阵
Matrix<double, Dynamic, Dynamic> matrix_Nd

// 等同于如下定义
MatrixXd matrix_NNd

// 定义一个动态大小的列向量
VectorXi vector_X1i

其中常量Dynamic为Eigen内置的值,为 -1

二、矩阵的属性与输入、输出

1.矩阵的行、列、大小

一个非动态大小的矩阵有自己固有的大小和行列,可以通过如下语句进行调用:

// 矩阵的行数
cout << matrix_33d.rows() << endl;
// 矩阵的列数
cout << matrix_33d.cols() << endl;
// 矩阵的元素个数
cout << matrix_33d.size() << endl;

对于动态大小的矩阵,可以使用resize()对其进行修改个数

// 定义一个初始大小为2*2的动态矩阵
MatrixXd matrix(2, 2);
// 修改其大小为3*3
matrix.resize(3, 3);

2.矩阵的输入

矩阵的输入格式如下:

matrix_33d << 1,2,3,
              4,5,6,
              7,8,9;

也可以通过索引的方式进行逐个赋值:

// 给第一行第一列的元素赋值
matrix_33d(0,0) = 1

3.矩阵的输出

输出矩阵的元素可以使用迭代的形式进行:

for(int i=0, i<matrix_33d.rows(), i++){
     
    for(int j=0, j<matrix_33d.cols(), j++){
     
        cout << matrix_33d(i, j)<< "\n";
    }
    cout << endl;
}

此外也可以直接输出整个举矩阵:

cout << matrix_33d <<endl;

4.矩阵初始化

在Eigen中声明矩阵后,可以将其初始化为零一矩阵、单位矩阵等。

# include
#include 
#include 

using namespace std;
using namespace Eigen;

int main(int argc, char **argv)
{
     
    // 初始化零矩阵
    Matrix3d matrix_33d_0 = Matrix3d::Zero();
    // 初始化一矩阵
    Matrix3d matrix_33d_1 = Matrix3d::Ones();
    // 单位矩阵
    Matrix3d matrix_33d_I = Matrix3d::Identity();
    // 随机矩阵,可填入参数:矩阵行数,矩阵列数
    // 生成-1~1的矩阵
    Matrix3d matrix_33d_random = Matrix3d::Random();    
    return 0;
}

三、矩阵的运算

1.基础运算

在Eigen库中,通过对四则算术运算符(+、-、*、/)重载实现了矩阵间的四则运算。由于向量即在某一维度上为1的矩阵,所以除了特殊的运算外不展开说明其他与矩阵相同的运算。

#include 

using namespace std;

#include 
#include 
 
using namespace Eigen;
 
int main(int argc, char **argv){
     
    // 定义矩阵
    Matrix3d matrix_33d = Matrix3d:Zero();
    a << 1, 2,
         3, 4;
    MatrixXd b(2,2);
    b << 2, 3,
        1, 4;
    // 矩阵间加减
    cout << "a + b =\n" << a + b << endl;
    cout << "a - b =\n" << a - b << endl;
    // 加减复合运算符
    cout << "Doing a += b;" << endl;
    a += b;
    cout << "Now a =\n" << a << endl;
    // 取反运算符
    cout << -a << endl;
    // 矩阵数乘\数除运算
    cout << "0.25 * a =\n" << 0.25 * a << endl;
    cout << "a / 4 =\n" << a / 4 << endl;
    // 矩阵间相乘
    cout << "a * a =\n" << a * a << endl;
    return 0;
}

需要注意的是Eigen不支持自动进行类型升级,若需要在不同数据类型的矩阵间进行运算,需要进行显式的数据转换。

// float和double类型间的运算需要显式的数据转换
Matrix<double, 2 ,3> result = matrix_23f. cast<double>()  * matrix_33d

2.矩阵自身运算

对于矩阵具备的转置、逆矩阵、共轭矩阵、迹等通过调用对应API即可。

cout << matrix_33d.transpose() << endl;			// 矩阵求转置
cout << matrix_33d.inverse() << endl;			// 矩阵求逆
cout << matrix_33d.conjugate()  << endl;		// 矩阵求共轭
cout << mtrix_33d.adjoint() << endl;			// 矩阵求伴随矩阵
cout << matrix_33d.trace() << endl;				// 矩阵求迹
cout << matrix_33d.determinant() << endl;		// 矩阵行列式

3.向量的叉乘和点乘

在Eigen库中使用dot()cross() 方法来实现向量的叉乘点乘

#include 
#include 
 
using namespace Eigen;
using namespace std;

int main(){
     
    Vector3d v(1,2,3);
    Vector3d w(0,1,2);
 
    cout << "向量点乘 " << v.dot(w) << endl;
    // 等同于将其中一个向量转换为其伴随矩阵,然后两个矩阵相乘
    double dp = v.adjoint()*w; 
    cout << dp << endl;
    cout << "向量叉乘\n" << v.cross(w) << endl;

4.矩阵的统计运算

Eigen库自带了用于统计运算的函数,如求和、求最大值、最小值等。

#include 
#include 
 
using namespace std;
using namespace Eigen;

int main(){
     
    Matrix2d mat;
    mat << 1, 2,
           3, 4;
    cout << "矩阵元素求和:" << mat.sum() << endl;
    cout << "矩阵元素求积:" << mat.prod()      << endl;
    cout << "矩阵元素取中位数:" << mat.mean()      << endl;
    cout << "矩阵元素取最小值:" << mat.minCoeff()  << endl;
    cout << "矩阵元素取最大值:" << mat.maxCoeff()  << endl;
}

5.矩阵的特征值和特征向量

在矩阵运算中,计算矩阵的特征值和特征向量十分重要,Eigen库中提供了如下方法,计算矩阵的特征值。

SelfAdjointEigenSolver<Matrix3d> eigensolver(matrix_33d.transpose() * matrix_33d)
cout << eigensolver.eigenvalues()	<< endl;       // 求矩阵特征值
cout << eigensolver.eigenvectors() << endl;        // 求矩阵特征向量

四、广播机制

广播构造了一个表达式,其中向量(列或行)通过在一个方向上复制从而形成矩阵。实现广播需要使用方法**.colwise()** 和 .rowwise()

#include 
#include 
 
using namespace std;
using namespace Eigen;

int main()
{
     
    MatrixXf mat(2,4);
    VectorXf v(2);
  
    mat << 1, 2, 6, 9,
           3, 1, 7, 2;     
    
    v_1 << 0,
           1;
    
    v_2 << 0,1,2,3;
       
    // 列向量广播
    mat.colwise() += v_1;
  
    cout << "Broadcasting result: " << endl;
    cout << mat << endl;
    /*
    * 输出结果如下:
    * Broadcasting result: 
    * 1 2 6 9
    * 4 2 8 3
    */
    
    // 行向量广播
    mat.rowwise() += v_2
    
    cout << "Broadcasting result: " << endl;
    cout << mat << endl;
    /*
    * 输出结果如下:
    * Broadcasting result: 
    * 1 3  8 12
    * 4 3 10  6
    */
}

上述代码即为实现将列向量进行广播,实现与矩阵相加。
在这里插入图片描述

你可能感兴趣的:(C++,c++)