C++ 的Eigen 库全解析

 更新时间:2026年06月25日 10:49:28   作者:卷无止境  
Eigen作为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 提供了大量预定义的便捷类型别名,规律如下:

类型名含义
Matrix3f3×3 的 float 矩阵
Matrix4d4×4 的 double 矩阵
MatrixXd动态大小的 double 矩阵(运行时确定)
Vector3f3维 float 列向量
VectorXd动态大小的 double 列向量
RowVector3d3维 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

参考来源:

 到此这篇关于C++ 的Eigen 库全解析的文章就介绍到这了,更多相关C++ Eigen库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Qt重写QStackedWidget模拟实现home界面滑动效果

    Qt重写QStackedWidget模拟实现home界面滑动效果

    这篇文章主要为大家详细介绍了Qt如何通过重写QStackedWidget模拟实现home界面滑动效果,文中的实现过程讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-11-11
  • C语言输出教学日历表的方法实例

    C语言输出教学日历表的方法实例

    最近帮朋友做一些C语言的练习题,期间遇到了个比较有意思的题目,下面这篇文章主要给大家介绍了关于C语言输出教学日历表的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 详解C语言求两个数的最大公约数及最小公倍数的方法

    详解C语言求两个数的最大公约数及最小公倍数的方法

    这篇文章主要介绍了C语言求两个数的最大公约数及最小公倍数的方法,辗转相除法和辗转相减法在解决这种问题时最常用到,需要的朋友可以参考下
    2016-03-03
  • Qt4和Qt5的信号和槽的使用区别

    Qt4和Qt5的信号和槽的使用区别

    本文主要介绍了Qt4 和 Qt5 的信号和槽的连接 connect 与断开 disconnect 区别,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • C++火车入轨算法的实现代码

    C++火车入轨算法的实现代码

    这篇文章主要介绍了C++火车入轨算法的实现代码,有需要的朋友可以参考一下
    2013-12-12
  • C语言复数的加减及输出结构体

    C语言复数的加减及输出结构体

    大家好,本篇文章主要讲的是C语言复数的加减及输出结构体,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • C语言关于二叉树中堆的创建和使用整理

    C语言关于二叉树中堆的创建和使用整理

    大家好,这里是针对二叉树中堆结构的顺序储存,整理出来一篇博客供我们一起复习和学习,如果文章中有理解不当的地方,还希望朋友们在评论区指出,我们相互学习,共同进步
    2022-08-08
  • C++中map和set的使用示例详解

    C++中map和set的使用示例详解

    set的底层是用红黑树实现的,增删查的效率是O(logn),迭代器遍历走的是搜索树的中序遍历,所以遍历后的元素是有序的,本文给大家介绍了C++中map和set的使用示例,感兴趣的朋友一起看看吧
    2025-05-05
  • 如何在C++中通过模板去除强制转换

    如何在C++中通过模板去除强制转换

    本文讲解的是如何在C++中通过模板去除强制转换,在编程工作中应尽量少使用强制类型转换,模板有助于我们实现这一目的,需要的朋友可以参考下
    2015-07-07
  • C语言返回值指针的函数详解

    C语言返回值指针的函数详解

    这篇文章主要为大家详细介绍了C语言返回值指针的函数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02

最新评论