C++ 的Eigen 库全解析
Eigen 是 C++ 世界里线性代数领域的"瑞士军刀"——纯头文件、零依赖、高性能,从简单的向量运算到复杂的矩阵分解,它几乎覆盖了数值计算的全部日常需求。无论你是做量化金融、机器学习、计算机图形学还是科学仿真,Eigen 都是绕不开的利器。
一、Eigen 是什么?
Eigen 是一个开源的 C++ 模板库,专门用于线性代数运算,包括矩阵、向量、数值求解器以及相关算法。它的核心特点是:
- 纯头文件:不需要编译成
.so或.lib,#include即用 - 表达式模板(Expression Templates) :利用惰性求值(lazy evaluation)避免不必要的临时对象,性能极佳
- 同时支持稠密矩阵与稀疏矩阵:覆盖绝大多数工程场景
- 丰富的矩阵分解:LU、Cholesky、QR、SVD 等应有尽有
- 行优先/列优先存储均可配置
相比自己手写矩阵类,Eigen 经过了大量"战场检验",bug 少、社区活跃、API 直观,是生产环境的不二选择。
二、安装与配置
Eigen 的安装是所有 C++ 库里最简单的之一——只需要下载头文件。
方法一:直接下载
从 eigen.tuxfamily.org 下载源码包,解压后将 Eigen/ 目录放到你的项目中或系统 include 路径下。
方法二:包管理器
# Ubuntu/Debian sudo apt install libeigen3-dev # macOS (Homebrew) brew install eigen
方法三:Git 子模块(推荐用于项目管理)
git submodule add https://gitlab.com/libeigen/eigen.git git submodule update --init --recursive
编译时只需加 -I 指定头文件路径:
g++ -I /path/to/eigen/ my_program.cpp -o my_program
不需要链接任何库文件,这一点非常省心。
三、核心概念:Matrix 类型系统
Eigen 的一切都建立在 Matrix<Scalar, Rows, Cols> 这个模板类上。理解它的命名规则,是入门的第一步。
3.1 类型命名规则
Eigen 提供了大量预定义的便捷类型别名,规律如下:
| 类型名 | 含义 |
|---|---|
| Matrix3f | 3×3 的 float 矩阵 |
| Matrix4d | 4×4 的 double 矩阵 |
| MatrixXd | 动态大小的 double 矩阵(运行时确定) |
| Vector3f | 3维 float 列向量 |
| VectorXd | 动态大小的 double 列向量 |
| RowVector3d | 3维 double 行向量 |
后缀规则:数字代表固定尺寸,X 代表动态,f/d/i/cf/cd 分别对应 float/double/int/复数float/复数double。
3.2 固定尺寸 vs 动态尺寸
// 固定尺寸:编译期已知,分配在栈上,速度更快 Eigen::Matrix3f A; Eigen::Vector4d v; // 动态尺寸:运行时确定,分配在堆上,更灵活 Eigen::MatrixXd M(100, 100); Eigen::VectorXd u(50); // 自定义任意尺寸 Eigen::Matrix<float, 20, 75> M2;
一般建议:尺寸小且固定时用固定类型(性能更好),尺寸大或不确定时用动态类型。
四、矩阵的初始化
4.1 逗号初始化
Eigen::Matrix3d A;
A << 1, 2, 3,
4, 5, 6,
7, 8, 9;
这种语法非常直观,<< 运算符按行填充元素。
4.2 特殊矩阵
// 零矩阵 Eigen::MatrixXd Z = Eigen::MatrixXd::Zero(3, 3); // 全1矩阵 Eigen::MatrixXd O = Eigen::MatrixXd::Ones(3, 3); // 单位矩阵 Eigen::Matrix3d I = Eigen::Matrix3d::Identity(); // 随机矩阵(元素在 [-1, 1] 之间) Eigen::MatrixXd R = Eigen::MatrixXd::Random(3, 3); // 常数矩阵 Eigen::MatrixXd C = Eigen::MatrixXd::Constant(3, 3, 6.28);
4.3 元素访问
Eigen::MatrixXd m(2, 2); m(0, 0) = 3.0; // 用圆括号,注意是0-indexed m(1, 0) = 2.5; double val = m(0, 1); // 向量可以用单下标 Eigen::VectorXd v(3); v(0) = 1.0; v(1) = 2.0; v(2) = 3.0;
五、基本运算
5.1 加减法与标量运算
Eigen::Matrix2d a, b; a << 1, 2, 3, 4; b << 2, 3, 1, 4; auto c = a + b; // 矩阵加法 auto d = a - b; // 矩阵减法 a += b; // 原地加法 auto e = a * 2.5; // 标量乘法 auto f = 0.1 * b; // 标量乘法(交换律) a *= 2; // 原地标量乘法
加减法要求两个矩阵形状完全一致,且 Eigen 不做自动类型提升,float 矩阵和 double 矩阵不能直接相加。
5.2 矩阵乘法
Eigen::MatrixXd A(3, 3), B(3, 3); // ... 初始化 ... auto C = A * B; // 矩阵乘法(不是逐元素!) auto v_out = A * v; // 矩阵-向量乘法
5.3 转置与共轭
Eigen::MatrixXd A(3, 3); auto AT = A.transpose(); // 转置 auto AC = A.conjugate(); // 共轭(实数矩阵等同于自身) auto AH = A.adjoint(); // 共轭转置(Hermitian) // 注意!不能原地转置:A = A.transpose() 是错误的! A.transposeInPlace(); // 正确的原地转置方式
5.4 点积与叉积
Eigen::Vector3d u(1, 2, 3), v(4, 5, 6); double dot_product = u.dot(v); // 点积:标量 Eigen::Vector3d cross = u.cross(v); // 叉积:仅限3维向量 double norm = u.norm(); // L2范数 double sq_norm = u.squaredNorm(); // 范数的平方
六、实用操作:块操作与切片
Eigen 提供了强大的子矩阵访问能力,这在实际工程中极为常用。
Eigen::MatrixXd M(4, 4); // ... 初始化 ... // 提取子矩阵:block(起始行, 起始列, 行数, 列数) auto sub = M.block(1, 1, 2, 2); // 从(1,1)开始的2×2子矩阵 // 提取行/列 auto row1 = M.row(0); // 第0行 auto col2 = M.col(2); // 第2列 // 提取角落 auto top_left = M.topLeftCorner(2, 2); auto bot_right = M.bottomRightCorner(2, 2); // 向量的头尾 Eigen::VectorXd v(6); auto head = v.head(3); // 前3个元素 auto tail = v.tail(3); // 后3个元素 auto seg = v.segment(1, 4); // 从index 1开始的4个元素
七、矩阵分解与线性方程求解
这是 Eigen 最强大的部分之一。求解线性方程组 Ax = b 有多种分解方式,精度和速度各有侧重。
7.1 常用分解方法对比
| 分解方法 | 适用场景 | 速度 | 精度 |
|---|---|---|---|
| PartialPivLU | 方阵,通用 | 快 | 好 |
| FullPivLU | 方阵,需要秩信息 | 较慢 | 极好 |
| LLT (Cholesky) | 对称正定矩阵 | 最快 | 好 |
| LDLT | 对称半正定矩阵 | 快 | 好 |
| HouseholderQR | 最小二乘,通用 | 中等 | 好 |
| JacobiSVD | 最小二乘,需奇异值 | 慢 | 最好 |
7.2 代码示例
#include <Eigen/Dense>
Eigen::Matrix3d A;
Eigen::Vector3d b;
A << 2, 1, -1,
-3, -1, 2,
-2, 1, 2;
b << 8, -11, -3;
// 方法1:LU 分解(通用方阵)
Eigen::Vector3d x1 = A.lu().solve(b);
// 方法2:Cholesky(对称正定矩阵,最快)
// Eigen::Vector3d x2 = A.llt().solve(b);
// 方法3:QR 分解(最小二乘问题)
Eigen::Vector3d x3 = A.colPivHouseholderQr().solve(b);
// 方法4:直接求逆(小矩阵可用,大矩阵不推荐)
Eigen::Matrix3d A_inv = A.inverse();
// 行列式
double det = A.determinant();
八、Array 类:逐元素运算
Eigen 区分了 Matrix(线性代数语义)和 Array(逐元素运算语义),这个设计非常精妙。
Eigen::ArrayXXd a(3, 3), b(3, 3); // ... 初始化 ... auto c = a * b; // 逐元素乘法(不是矩阵乘法!) auto d = a / b; // 逐元素除法 auto e = a.sqrt(); // 逐元素开方 auto f = a.exp(); // 逐元素指数 auto g = a.pow(2.0); // 逐元素幂次 auto h = (a > 0.5); // 逐元素比较,返回 bool Array // Matrix 和 Array 之间的转换 Eigen::MatrixXd M = a.matrix(); // Array → Matrix Eigen::ArrayXXd A2 = M.array(); // Matrix → Array
一个常见的混合用法:先做矩阵乘法,再对结果逐元素应用激活函数(比如 ReLU):
Eigen::MatrixXd result = (W * x).array().max(0.0).matrix();
九、一个完整的入门示例
把前面所有知识串联起来,下面是一个完整可运行的程序:
#include <iostream>
#include <Eigen/Dense>
int main() {
// 1. 创建矩阵
Eigen::MatrixXd A = Eigen::MatrixXd::Random(3, 3);
A = A.transpose() * A; // 构造对称正定矩阵
Eigen::VectorXd b = Eigen::VectorXd::Random(3);
std::cout << "矩阵 A:\n" << A << "\n\n";
std::cout << "向量 b:\n" << b << "\n\n";
// 2. 求解线性方程组 Ax = b
Eigen::VectorXd x = A.llt().solve(b);
std::cout << "解 x (Cholesky):\n" << x << "\n\n";
// 3. 验证:计算残差
double residual = (A * x - b).norm();
std::cout << "残差 ||Ax - b|| = " << residual << "\n\n";
// 4. 特征值分解
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(A);
std::cout << "特征值:\n" << solver.eigenvalues() << "\n\n";
std::cout << "特征向量:\n" << solver.eigenvectors() << "\n";
return 0;
}
编译运行:
g++ -O2 -I /path/to/eigen/ main.cpp -o demo && ./demo
十、进阶方向
掌握了上面的基础之后,Eigen 还有几个值得深入的方向:
- 稀疏矩阵模块(
Eigen/Sparse):适合有限元、图算法等大规模稀疏问题,提供SparseMatrix<double>类型和配套的稀疏求解器 - 几何模块(
Eigen/Geometry):四元数、旋转矩阵、仿射变换,是机器人和图形学的标配 - 与其他库集成:Eigen 可以与 OpenCV、PCL、ROS 无缝对接,很多库内部直接使用 Eigen 类型
- 性能调优:开启
-O2或-O3编译优化,配合 SIMD 指令(Eigen 会自动利用 SSE/AVX),性能可以媲美手写 BLAS
参考来源:
- Eigen 官方文档 — Getting Started
- QuantStart — Eigen Library for Matrix Algebra in C++
- Brown CS224 — Eigen Tutorial (GitHub)
- Eigen 官方文档 — Matrix and Vector Arithmetic
到此这篇关于C++ 的Eigen 库全解析的文章就介绍到这了,更多相关C++ Eigen库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Qt重写QStackedWidget模拟实现home界面滑动效果
这篇文章主要为大家详细介绍了Qt如何通过重写QStackedWidget模拟实现home界面滑动效果,文中的实现过程讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下2022-11-11


最新评论