Eigen 矩阵操作 | 转置 / 逆 / 伴随 / 特征值

注:本文为 “Eigen 矩阵操作 ” 相关合辑。
略作重排,未整理去重。
如有内容异常,请看原文。


Eigen 库矩阵基本操作:转置矩阵、逆矩阵、伴随矩阵与特征值分析

齐天大圣~~ 于 2018-09-03 10:54:03 发布

一、代码实现

以下代码基于 Eigen 库,实现了 3 阶实矩阵的定义、初始化及核心运算,包括转置、伴随、求逆、行列式计算与特征值 / 特征向量求解。

#include <iostream>
#include "Eigen\Dense"  // 包含 Eigen 库中稠密矩阵相关类与函数
using namespace Eigen;  // 引入 Eigen 命名空间,简化代码调用
using namespace std;    // 引入标准输入输出命名空间

int main()
{
    // 定义并初始化 3×3 双精度实矩阵 Mat1
    Matrix3d Mat1;  // Matrix3d 表示 3 阶双精度(double)稠密矩阵
    Mat1 << 1, 2, 3,  // 按行赋值:第 1 行元素为 [1, 2, 3]
          4, 6, 8,  // 第 2 行元素为 [4, 6, 8]
          7, 9, 9;  // 第 3 行元素为 [7, 9, 9]

    // 输出原始矩阵
    cout << "Mat1 = \n" << Mat1 << endl;

    // 计算并输出转置矩阵
    cout << "Mat1 转置矩阵:\n" << Mat1.transpose() << endl;

    // 计算并输出伴随矩阵
    cout << "Mat1 伴随矩阵:\n" << Mat1.adjoint() << endl;

    // 计算并输出逆矩阵
    cout << "Mat1 逆矩阵:\n" << Mat1.inverse() << endl;

    // 计算并输出行列式
    cout << "Mat1 行列式:\n" << Mat1.determinant() << endl;

    // 求解特征值与特征向量(针对自伴随矩阵)
    SelfAdjointEigenSolver<Matrix3d> eigensolver(Mat1);  // 定义特征值求解器
    if (eigensolver.info() != Success) abort();  // 检查求解是否成功,失败则终止程序

    // 输出特征值与特征向量
    cout << "特征值:\n" << eigensolver.eigenvalues() << endl;
    cout << "特征向量:\n" << eigensolver.eigenvectors() << endl;

    return 0;
}

二、代码解析

(一)矩阵定义与初始化

  • 类型说明Matrix3d 是 Eigen 库预定义的类型,对应 3 阶双精度实矩阵(维度固定为 3×3,元素类型为 double)。
  • 赋值方式:通过流操作符 << 按“行优先”顺序赋值,即先输入第 1 行所有元素,再输入第 2 行,以此类推。

(二)矩阵转置(Transpose)

  • 函数调用Mat1.transpose()
  • 数学定义:对于 m×n 矩阵 A = ( a i j ) \mathbf{A} = (a_{ij}) A=(aij),其转置矩阵 A T \mathbf{A}^\mathrm{T} AT 是 n×m 矩阵,满足 ( A T ) i j = a j i (\mathbf{A}^\mathrm{T})_{ij} = a_{ji} (AT)ij=aji(即第 i 行第 j 列元素等于原矩阵第 j 行第 i 列元素)。
  • 示例:若 M a t 1 = ( 1 2 3 4 6 8 7 9 9 ) \mathbf{Mat1} = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 6 & 8 \\ 7 & 9 & 9 \end{pmatrix} Mat1= 147269389 ,则其转置矩阵为 ( 1 4 7 2 6 9 3 8 9 ) \begin{pmatrix} 1 & 4 & 7 \\ 2 & 6 & 9 \\ 3 & 8 & 9 \end{pmatrix} 123468799

(三)矩阵伴随(Adjoint)

  • 函数调用Mat1.adjoint()
  • 数学定义
    • 对于 复矩阵:伴随矩阵 = 共轭转置矩阵(先对每个元素取共轭,再做转置);
    • 对于 实矩阵(如代码中的 Mat1):共轭操作不改变元素值,因此伴随矩阵 等价于转置矩阵
  • 适用场景:主要用于复矩阵运算(如量子力学、信号处理),实矩阵场景下可替代转置,但语义上更强调“伴随”的数学属性。

(四)矩阵求逆(Inverse)

  • 函数调用Mat1.inverse()
  • 数学定义:对于 n 阶方阵 A \mathbf{A} A,若存在 n 阶方阵 A − 1 \mathbf{A}^{-1} A1 满足 A A − 1 = I \mathbf{A}\mathbf{A}^{-1} = \mathbf{I} AA1=I I \mathbf{I} I 为 n 阶单位矩阵),则 A − 1 \mathbf{A}^{-1} A1 称为 A \mathbf{A} A 的逆矩阵。
  • 存在条件:矩阵可逆的充要条件是其行列式 det ⁡ ( A ) ≠ 0 \det(\mathbf{A}) \neq 0 det(A)=0(即矩阵非奇异)。
  • Eigen 特性:Eigen 会自动检查矩阵是否可逆,若不可逆(奇异矩阵),调用 inverse() 会返回数值不稳定的结果,建议先通过 determinant() 验证行列式非零。

(五)行列式(Determinant)

  • 函数调用Mat1.determinant()
  • 数学定义:n 阶方阵 A \mathbf{A} A 的行列式是一个标量,记为 det ⁡ ( A ) \det(\mathbf{A}) det(A) ∣ A ∣ |\mathbf{A}| A,反映矩阵的“缩放能力”(如线性变换中面积 / 体积的缩放比例)。
  • 3 阶矩阵行列式公式:对于 A = ( a b c d e f g h i ) \mathbf{A} = \begin{pmatrix} a & b & c \\ d & e & f \\ g & h & i \end{pmatrix} A= adgbehcfi ,其行列式为:
    det ⁡ ( A ) = a ( e i − f h ) − b ( d i − f g ) + c ( d h − e g ) \det(\mathbf{A}) = a(ei - fh) - b(di - fg) + c(dh - eg) det(A)=a(eifh)b(difg)+c(dheg)
  • 代码计算结果 det ⁡ ( M a t 1 ) = 1 × ( 6 × 9 − 8 × 9 ) − 2 × ( 4 × 9 − 8 × 7 ) + 3 × ( 4 × 9 − 6 × 7 ) = 4 \det(\mathbf{Mat1}) = 1 × (6 × 9 - 8 × 9) - 2 × (4 × 9 - 8 × 7) + 3 × (4 × 9 - 6 × 7) = 4 det(Mat1)=1×(6×98×9)2×(4×98×7)+3×(4×96×7)=4(非零,故矩阵可逆)。

(六)特征值与特征向量(Eigenvalue & Eigenvector)

  • 求解类说明SelfAdjointEigenSolver<Matrix3d> 是 Eigen 中用于求解 自伴随矩阵(实对称矩阵是自伴随矩阵的特例)特征值与特征向量的工具类,求解精度高且数值稳定。
  • 数学定义:对于 n 阶方阵 A \mathbf{A} A,若存在标量 λ \lambda λ(特征值)和非零向量 v \mathbf{v} v(特征向量)满足:
    A v = λ v \mathbf{A}\mathbf{v} = \lambda\mathbf{v} Av=λv
    则称 λ \lambda λ A \mathbf{A} A 的特征值, v \mathbf{v} v λ \lambda λ 对应的特征向量。
  • 求解结果说明
    • eigensolver.eigenvalues():返回一个列向量,包含矩阵的所有特征值(自伴随矩阵的特征值均为实数);
    • eigensolver.eigenvectors():返回一个矩阵,每一列对应一个特征值的单位特征向量(不同特征值的特征向量相互正交)。
  • 合法性检查eigensolver.info() != Success 用于判断特征值分解是否成功(如矩阵维度不匹配、数值奇异等会导致失败),失败时调用 abort() 终止程序,避免后续错误计算。

三、代码注意事项

  1. 头文件依赖:代码中 #include "Eigen\Dense" 不可省略,该头文件包含了 Eigen 库中稠密矩阵的所有核心功能(如矩阵运算、特征值求解等)。
  2. 命名空间使用using namespace Eigen;using namespace std; 简化了代码调用(无需每次写 Eigen::Matrix3dstd::cout),但在大型项目中建议减少全局命名空间的使用,避免命名冲突。
  3. 矩阵维度兼容性:Eigen 会在编译阶段检查矩阵运算的维度兼容性(如转置矩阵与原矩阵的乘法),若维度不匹配会报编译错误,需确保运算符合线性代数规则。
  4. 数值稳定性:对于非自伴随矩阵(非对称矩阵),应使用 EigenSolver<Matrix3d> 类求解特征值与特征向量,而非 SelfAdjointEigenSolver,否则会导致求解结果错误。

Eigen中transposeInPlace()transpose()的使用详解和示例代码

点云SLAM于 2025-06-30 15:23:04 发布

在 Eigen 中,transpose()transposeInPlace() 均用于矩阵转置,但其使用方式和应用场景存在差异。以下将详细阐释两者的区别、适用场景及示例代码。

一、函数区别总结

特性transpose()transposeInPlace()
类型成员函数成员函数
返回值返回转置副本无返回值
是否修改原矩阵❌ 否(保留原矩阵)✅ 是(原地转置)
适用于方阵
适用于非方阵❌ 仅适用于方阵
使用场景保留原始数据,创建新矩阵需要节省内存,原地修改矩阵

二、详细用法和示例

1. transpose() —— 返回一个转置后的新矩阵

示例

#include <Eigen/Dense>
#include <iostream>

int main() {
    Eigen::Matrix3f mat;
    mat << 1, 2, 3,
           4, 5, 6,
           7, 8, 9;

    Eigen::Matrix3f matT = mat.transpose();  // 返回新矩阵

    std::cout << "Original:\n" << mat << "\n";
    std::cout << "Transposed:\n" << matT << "\n";
    return 0;
}

输出

Original:
1 2 3
4 5 6
7 8 9
Transposed:
1 4 7
2 5 8
3 6 9

适合保留原始矩阵的应用场景,例如图像处理、缓存优化等。

2. transposeInPlace() —— 原地修改矩阵,仅限方阵

示例

#include <Eigen/Dense>
#include <iostream>

int main() {
    Eigen::Matrix3f mat;
    mat << 1, 2, 3,
           4, 5, 6,
           7, 8, 9;

    mat.transposeInPlace();  // 原地转置(只适用于方阵)

    std::cout << "In-place Transposed:\n" << mat << "\n";
    return 0;
}

输出

In-place Transposed:
1 4 7
2 5 8
3 6 9

注意:非方阵(如 Matrix3x4f)调用 transposeInPlace() 会触发编译错误。

三、使用建议

使用目标建议函数
不改变原始矩阵,需临时结果transpose()
内存敏感,处理的是方阵transposeInPlace()
处理非方阵只能用 transpose()

四、非方阵的 transpose 示例

Eigen::Matrix<float, 2, 3> mat;
mat << 1, 2, 3,
       4, 5, 6;

Eigen::Matrix<float, 3, 2> matT = mat.transpose();

不能写成:

mat.transposeInPlace(); // ❌ 编译错误!

五、结合 memcpy 用法示例

Eigen::Matrix4f mat;
// ... fill mat
float* buffer = new float[16];
// 如果目标数组是 row-major,需转置
Eigen::Matrix4f temp = mat.transpose();
std::memcpy(buffer, temp.data(), sizeof(float) * 16);

总结

  • transpose() ➜ 返回副本,不修改原矩阵,适用于所有维度。
  • transposeInPlace() ➜ 原地转置,仅限方阵,节省内存。
  • 非方阵请勿使用 transposeInPlace()
  • 可与 memcpy 配合用于与 OpenGL / DirectX / 自定义矩阵格式对接。

Eigen 踩坑:Matrix 的 transpose(矩阵转置)计算之后不能赋值给自身

HW140701 于 2025-06-30 15:23:04 发布

在使用 Eigen 线性代数库进行矩阵操作时,发现一个关键差异:矩阵 Matrixtranspose()(转置)计算结果不能直接赋值给原矩阵自身,这与 C/C++ 中常规变量的赋值逻辑不同,核心原因在于 Eigen 对操作符重载的特殊设计,需重点注意以避免程序异常。

一、问题现象:直接赋值导致程序崩溃

1. 常规 C/C++ 赋值逻辑(可正常运行)

在 C/C++ 中,对普通变量执行“计算后赋值给自身”的操作是常规写法,例如:

int a = 10;
a = a / 10;  // 结果为 a=1,无异常

2. Eigen 矩阵转置的错误写法(触发崩溃)

但在 Eigen 中,若将矩阵转置结果直接赋值给原矩阵,会导致程序崩溃,代码示例如下:

Eigen::MatrixXd A = B * C;  // B、C 为已定义的 Eigen 矩阵
A = A.transpose();          // 错误:直接将转置结果赋值给原矩阵 A

3. 崩溃时的错误弹窗

程序崩溃时会弹出 Microsoft Visual C++ 运行时库的断言失败提示,核心错误信息如下(对应文档中插入的错误截图):

  • 错误来源LocomotionController.dll 中调用了 Eigen 的 transpose.h 文件
  • 错误位置transpose.h 第 378 行
  • 核心断言(!check_transpose_aliasing_run_time_selector<...>::run(...)) && "aliasing detected during transposition..."
  • 错误原因:检测到“别名冲突(aliasing)”——即转置操作与原矩阵的内存访问存在冲突,导致未定义行为。

在这里插入图片描述

二、问题根源:Eigen 的“表达式模板”设计

崩溃的核心原因是 Eigen 采用了表达式模板(Expression Templates) 优化机制:

  • Eigen 的 transpose() 方法不直接返回新的矩阵对象,而是返回一个“转置表达式对象”(仅记录转置操作的逻辑,不立即执行计算、不分配新内存)。
  • 当执行 A = A.transpose() 时,Eigen 会尝试直接在原矩阵 A 的内存空间上进行转置操作,但转置过程中需要读取原矩阵的元素,而赋值操作又会覆盖原内存——这种“边读边写”的冲突触发了别名检测断言,最终导致程序崩溃。

三、解决方案:两种正确的实现方式

1. 方案一:通过临时矩阵中转(间接赋值)

先将原矩阵的副本进行转置,再将结果赋值给目标矩阵(避免原内存冲突),代码示例:

Eigen::MatrixXd A;
Eigen::MatrixXd Acopy = B * C;  // 第一步:先创建原矩阵(B*C)的副本 Acopy
A = Acopy.transpose();          // 第二步:将副本的转置结果赋值给 A,无冲突

2. 方案二:使用 Eigen 内置的 transposeInPlace() 方法(推荐)

Eigen 官方提供了专门用于“原地转置”的方法 transposeInPlace(),该方法会安全地在原矩阵内存中执行转置操作,无需额外创建临时矩阵,代码示例:

Eigen::MatrixXd A = B * C;
A.transposeInPlace();  // 正确:原地执行转置,直接修改 A 的值,无内存冲突

注:查阅 Eigen 官方文档可知,transposeInPlace() 是为“矩阵自身转置”场景设计的安全接口,已内部处理了内存访问冲突问题。

四、注意事项

  1. 避免“自身转置后赋值”:永远不要写 A = A.transpose(),需改用 A.transposeInPlace()
  2. 理解表达式模板特性:Eigen 中类似 transpose() 的操作(如 inverse()dot() 等)多返回表达式对象,而非直接返回计算结果,需注意赋值目标是否与原矩阵冲突。
  3. 优先使用官方接口:对于“原地修改矩阵”的需求,优先选择 Eigen 提供的 InPlace 后缀方法(如 transposeInPlace()inverseInPlace()),确保操作安全。

记录此问题及解决方案,以防后续开发中再次因 Eigen 的特殊设计导致类似崩溃。


via:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值