介绍
Eigen 3 是一个以纯泛型编写的 C++ 矩阵运算库,它的授权是 MPL2,以源代码的形式提供给用户,所以只要把它的代码 include 进自己的程序就可以使用,不需要链接 DLL,也没有任何除 iostream 以外的依赖项。
实用链接
例程
下面是一个比较全面的示例,我写了较多的注释来澄清一些上手时容易误会的问题,旨在帮助你在一个小时内学会 Eigen 的常用操作并开始工作,也补足了被 AsciiQuickReference.txt 忽视的一些要点。
// 必须的库:
#include <iostream>
#include <Eigen/Core>
// 本例涉及到的功能还需要:
#include <Eigen/LU>
#include <Eigen/Geometry>
// 使用的命名空间:
using namespace Eigen;
using namespace std;
int main()
{
/* --------------------------------------------------------------- */
/* 1. 矩阵声明 */
Matrix4d A; // 声明 4x4 的 double 矩阵 A,“4d”中 4 代表
// 阶数,d 代表元素的数据类型
MatrixXd B(4, 12); // 声明动态大小的 double 矩阵 B,并指定尺寸为
// 4 行 12 列(4x12)
MatrixXd C, D; // 声明动态大小、未定尺寸的 double 矩阵 C、D
Matrix<int, 2, 3> E;// 声明 2x3 的 int 矩阵 E
VectorXd vA, vB; // 声明动态大小、未定尺寸的 double 列向量
RowVectorXd rvB; // 声明动态大小、未定尺寸的 double 行向量 rvB
/// NOTE: 动态大小的矩阵,如果未指定尺寸,则在内存中初始大小为 0x0,
/// 可以后续用矩阵尺寸变换来更改。
/// NOTE: 未进行初始化的矩阵,其元素被用随机数填充。
/* --------------------------------------------------------------- */
/* 2. 矩阵初始化 */
A << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
/// NOTE: 方法 1,用移位运算符按先行后列对矩阵初始化(符合自然书写)。
/// WARNING: 数据不足或超出矩阵元素数,都会导致程序闪退,返回值 3。
cout << "\nA:\n" << A << endl; // 直接移位打印输出矩阵
B << A, A, A;
/// NOTE: 方法 2,用移位运算符将多个矩阵排入左值矩阵中,将按照自然
/// 书写的顺序设法将右值列表排入,如本例中 B 将由 3 个 A 横向排列构
/// 成;如果 B 是 12x4 矩阵,执行本句后 B 将由 3 个 A 纵向排列构成。
/// WARNING: 数据不足或超出矩阵元素数,都会导致程序闪退,返回值 3;
/// 因此用移位运算符对一个未定大小(0x0 大小)或元素总数与右值各矩阵
/// 元素总数不匹配的左值进行初始化,程序也会闪退。
cout << "\nB:\n" << B << endl;
A << (Matrix2i() << 1, 2, 3, 4).finished().cast<double>(),
MatrixXd::Zero(2, 2),
Matrix2d::Zero(),
MatrixXd::Identity(2, 2);
/// NOTE: 方法 3,用多种方法形成子矩阵,把它们排入左值矩阵中。
/// 其中:
/// 第一个子矩阵用 finished() 将已初始化的匿名对象返回,再用
/// cast<type>() 方法转换为 double 矩阵(纯粹为了演示);
/// 第二个子矩阵用 MatrixXd 类的 Zero() 返回指定大小的零阵;
/// 第三个子矩阵与前一子矩阵的表达方式类似,而 Matrix2d 类已有
/// 尺寸,无需再规定尺寸;
/// 第四个子矩阵用 MatrixXd 类的 Identity() 返回单位阵。
cout << "\nA:\n" << A << endl;
C = A;
/// NOTE: 方法 4,用等号赋值,将右值拷贝到左值中,左值会根据右值的
/// 尺寸进行隐式的相似塑形(见下文),前提是左值必须为动态大小,且能
/// 够变为右值的尺寸;否则,程序闪退,返回值 3。
C = A + MatrixXd::Ones(A.rows(), A.cols());
/// NOTE: 在用等号赋值时,右值也可以是表达式。本例用 MatrixXd 类的
/// Ones() 静态方法返回与 A 同尺寸的全 1 阵,
cout << "\nC:\n" << C << endl;
A.fill(2);
/// NOTE: 方法 5,用 fill() 方法直接将矩阵用某个数值填充。
cout << "\nA (fill):\n" << A << endl;
A.setRandom();
/// NOTE: 方法 6,用 set____() 族方法将矩阵置为某种特殊矩阵:
/// setRandom() (-1, 1) 之间的随机数矩阵
/// setIdentity() 单位阵(非方阵时仅主对角线置 1,其他补 0)
/// setZero() 零阵
/// setOnes() 全 1 阵
///
/// setLinSpaced(size, low, high)
/// 向量专用,与 MATLAB 中 linespace() 类似
cout << "\nA (setRandom):\n" << A << endl;
/* --------------------------------------------------------------- */
/* 3. 矩阵尺寸变换 */
B.resize(12, 4);
/// NOTE: 硬塑形,将 B 大小设置为 12 行 4 列,该方法将原矩阵的所有
/// 元素按列排成向量,再将其按列赋予新矩阵(观察运行结果)。
/// WARNING: 如果原矩阵的元素总数与新矩阵元素总数不等,则所有数据丢
/// 失,新矩阵用随机值填充。
/// WARNING: 所有尺寸变换操作,都只能对动态大小的矩阵,或某些维度上
/// 尺寸为动态的矩阵进行;对诸如 Matrix3d 等静态大小的矩阵进行尺寸
/// 变换将导致程序闪退,返回值 3。
cout << "\nB (resize):\n" << B << endl;
D.resize(3, 3);
/// NOTE: 硬塑形的用例:D 未被初始化,声明时也未指定尺寸,在准备用移
/// 位运算符给 D 赋值前必须用 resize() 指定大小,否则存在前述元素数
/// 不匹配问题,引起闪退。
/// NOTE: 如果某个尺寸保持不变,仅变动另一尺寸,可将不作改变的尺寸以
/// Eigen::NoChange 代替(后续有例子);此时所有数据亦丢失。
D << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "\nD:\n" << D << endl;
vA.resize(5);
/// NOTE: 向量的硬塑形,只需规定长度即可。
vA << 1, 2, 3, 4, 5;
cout << "\nvA:\n" << vA << endl;
D.conservativeResize(2, Eigen::NoChange);
/// NOTE: 软塑形,将 D 大小设置为 2 行 ~ 列,该方法试图不改变原矩阵
/// 原有的元素,并变更其尺寸。缩小尺寸时将对矩阵进行简单剪裁,扩大尺
/// 寸时多出的元素用随机值填充。使用该方法改变矩阵尺寸时能保留数据。
/// NOTE: 向量也可进行类似操作。
cout << "\nD (conservativeResize):\n" << D << endl;
D.resizeLike(A);
/// NOTE: 相似塑形,将 D 的尺寸设置为与参数矩阵的尺寸一致。该方法的
/// 行为类似硬塑形,即当新旧矩阵元素数不一致时,所有数据丢失;否则,
/// 将用与硬塑形完全一样的方法将原矩阵的元素赋予新矩阵。
/// NOTE: 向量也可进行类似操作。
cout << "\nD (resizeLike A):\n" << D << endl;
/* --------------------------------------------------------------- */
/* 4. 矩阵访问 */
cout << "\nC:\n" << C << endl;
cout << "\nC(2, 1) = " << C(1, 0) << endl;
/// NOTE: 元素访问,语法与 MATLAB 一致,但由于 MATLAB 和自然书写习
/// 惯是从 1 起,而 C++ 的语法是从 0 起的,因此访问时实际编号要减 1。
cout << "\nC(2, :):\n" << C.row(1) << endl;
cout << "\nC(:, 3):\n" << C.col(2) << endl;
/// NOTE: 片段访问,语法与 MATLAB 的对应关系如下:
/// Eigen Matlab
/// x.head(n) // x(1: n)
/// x.tail(n) // x(end-n+1: end)
/// x.segment(i, n) // x(i+1: i+n)
/// P.block(i, j, rows, cols) // P(i+1:i+rows, j+1:j+cols)
/// P.row(i) // P(i+1, :)
/// P.col(j) // P(:, j+1)
/// R.row(i) = P.col(j) // R(i, :) = P(:, j)
/// P.leftCols(cols) // P(:, 1:cols)
/// P.middleCols(j, cols) // P(:, j+1:j+cols)
/// P.rightCols(cols) // P(:, end-cols+1:end)
/// P.topRows(rows) // P(1:rows, :)
/// P.middleRows(i, rows) // P(i+1:i+rows, :)
/// P.bottomRows(rows) // P(end-rows+1:end, :)
/// P.topLeftCorner(rows, cols) // P(1:rows, 1:cols)
/// P.bottomLeftCorner(rows, cols) // P(end-rows+1:end, 1:cols)
/// ______Corner(rows, cols)...
/* --------------------------------------------------------------- */
/* 5. 矩阵操作 */
D.resize(3, 3);
D << 2, 1, 0,
0, 2, 0,
0, 0, 2;
cout << "\nD = \n" << D << endl;
cout << "\nD.' = \n" << D.transpose() << endl;
cout << "\nD' = \n" << D.adjoint() << endl;
/// NOTE: 矩阵的转置、共轭转置操作。
cout << "\nsum(D(:)) = \n" << D.sum() << endl;
cout << "\nsum(D) = \n" << D.colwise().sum() << endl;
cout << "\nsum(D')' = \n" << D.rowwise().sum() << endl;
cout << "\nprod(D(:)) = \n" << D.prod() << endl;
cout << "\nprod(D) = \n" << D.colwise().prod() << endl;
cout << "\nprod(D')' = \n" << D.rowwise().prod() << endl;
cout << "\nmean(D(:)) = \n" << D.mean() << endl;
cout << "\nmean(D) = \n" << D.colwise().mean() << endl;
cout << "\nmean(D')' = \n" << D.rowwise().mean() << endl;
cout << "\ntrace(D) = \n" << D.trace() << endl;
/// NOTE: 矩阵的求和、连乘、均值、求迹操作与 MATLAB 语法的对应。
cout << "\nD + E = \n" << D + Matrix3d::Identity() << endl;
cout << "\nD - E = \n" << D - Matrix3d::Identity() << endl;
cout << "\nD * E = \n" << D * Matrix3d::Identity() << endl;
/// NOTE: 矩阵的加、减、乘操作,均与 MATLAB 语法一致。
C = D.array() * 2;
cout << "\nD .* 2 = \n" << C << endl;
C = C.array().sqrt();
cout << "\nsqrt(D.*2) = \n" << C << endl;
/// NOTE: 矩阵的元素运算操作,均可通过 matrix.array() 及相关方法或
/// 运算符实现。
/// 提供的方法包括:sin() cos() sqrt() abs() abs2() log() exp()
/// pow(n) square() cube() 以及行为类似 MATLAB 中 max/min 函数的
/// max(other_matrix.array()) 和 min(other_matrix.array())。
cout << "\ninv(D) = \n" << D.inverse() << endl;
cout << "\n|D| = " << D.determinant() << endl; // MATLAB: det(D)
/// NOTE: 矩阵的求逆、求行列式操作,需要 #include <Eigen/LU>
vA.resize(3); vB.resize(3);
vA << 1, 4, 9;
vB << 1, 1, 1;
cout << "\nvA = (" << vA.transpose() << ")'" << endl;
cout << "\nvB = (" << vB.transpose() << ")'" << endl;
cout << "\nmean(vA) = " << vA.mean() << endl;
cout << "\nnorm(vB) = " << vB.norm() << endl;
cout << "\nvA dot vB = " << vA.dot(vB) << endl;
Vector3d v3A, v3B;
v3A = vA; v3B = vB;
cout << "\nvA cross vB = (" << v3A.cross(v3B) << ")'" << endl;
/// NOTE: 向量的内积(点乘)、外积(叉乘)、均值和范数,
/// 其中计算外积需要 #include <Eigen/Geometry>,且被操作向量必须为
/// Vector3_ 类,对动态大小的向量求叉乘会出现编译错误。
system("pause");
return 0;
}
请编译运行这个程序,并对照运行结果理解代码。