C++矩阵库 | Eigen 3.4.90 | 中文使用文档 (一)

写在前面:我在学习Eigen库时,没找到好的中文文档,因此萌发了汉化Eigen官网文档的想法。其中一些翻译可能不是特别准确,欢迎批评指正。感兴趣的同学可以跳转到官网查看原文:Eigen: Dense matrix and array manipulation     

Eigen库,是一个开源的C++模板库,专门用于线性代数计算。本文只讲运用,不讨论安装问题。如果还未安装Eigen,请先自行安装。共分为四大部分,这篇文章属于第一份部分,主要介绍Eigen中常用的矩阵和数组操作。  

第二部分:C++矩阵库:Eigen 3.4.90 中文使用文档(二)-CSDN博客

第三部分:C++矩阵库:Eigen 3.4.90 中文使用文档(三)-CSDN博客

第四部分:待续...

一、模版类:Matrix

1、Matrix 模板类的前三个模板参数

Matrix 类总共有六个模板参数,但我们只需先了解前三个参数,剩下的三个参数有默认值,可以暂时忽略。

Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
  • Scalar: 标量类型,即系数的类型。例如,如果你需要一个浮点数矩阵,可以选择 float 类型。
  • RowsAtCompileTimeColsAtCompileTime: 在编译时已知的矩阵行数和列数。

2、便捷类型定义

Eigen提供了许多便捷的类型定义来简化常用矩阵和向量的定义。例如 Matrix4f 表示 4x4 的浮点数矩阵,定义如下: 

typedef Matrix<float, 4, 4> Matrix4f;
3、向量

在Eigen中,向量其实可以看做是具有一行或者一列的矩阵。其中列向量是最常见的。例如 Vector3f 其实是一个利用 typedef 定义的3维浮点数列向量,定义如下:

typedef Matrix<float, 3, 1> Vector3f;

Eigen也提供了行向量的便捷类型定义,其本质上也是一个矩阵。例如一个2维的整形行向量其实本质上也是一个矩阵。定义如下:

typedef Matrix<int, 1, 2> RowVector2i;
4、动态尺寸

Eigen不仅限于在编译时创建已知尺寸的矩阵和向量。模板参数 RowsAtCompileTimeColsAtCompileTime 可以取特殊值 Dynamic,表示尺寸在运行时确定。例如,MatrixXd 表示动态尺寸的双精度矩阵,定义如下:     

typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
5、构造函数

默认构造函数不进行动态内存分配,也不初始化矩阵系数。例如:                                                 

Matrix3f a;   // 一个 3x3 的未初始化浮点数矩阵
MatrixXf b;   // 一个动态尺寸的未初始化浮点数矩阵,当前尺寸为 0x0

还可以使用带尺寸的构造函数来分配系数数组,但不初始化系数

MatrixXf a(10, 15);  // 一个 10x15 的未初始化浮点数矩阵
VectorXf b(30);      // 一个尺寸为 30 的未初始化浮点数向量

可以使用初始化列表来初始化矩阵和向量的系数:

Vector2d a(5.0, 6.0);   // 列向量
Vector3d b(5.0, 6.0, 7.0); // 列向量

MatrixXi m {
  {1, 2}, 
  {3, 4}
};
6、系数访问器

Eigen 提供了重载的括号操作符来访问和修改系数。对于矩阵,先行后列,如果你安装了Eigen你可以执行下面的代码。例如:

Eigen::MatrixXd m(2, 2);
m(0, 0) = 3;
m(1, 0) = 2.5;
m(0, 1) = -1;
m(1, 1) = m(1, 0) + m(0, 1);
std::cout << "Here is the matrix m:\n" << m << std::endl;
7、大小调整

可以使用 resize() 方法调整动态尺寸矩阵的大小。例如:

Eigen::MatrixXd m(2, 5);
m.resize(4, 3);
std::cout << "The matrix m is of size " << m.rows() << "x" << m.cols() << std::endl;
std::cout << "It has " << m.size() << " coefficients" << std::endl;

Eigen::VectorXd v(2);
v.resize(5);
std::cout << "The vector v is of size " << v.size() << std::endl;

但是对于固定大小的矩阵和向量,resize() 方法的操作是无效的。

Eigen::Matrix4d m;
m.resize(4, 4); // 无操作
std::cout << "The matrix m is of size " << m.rows() << "x" << m.cols() << std::endl;
8、固定尺寸与动态尺寸

对于使用固定尺寸还是动态尺寸,官方给的建议是:在尺寸非常小(如小于 16)且已知的情况下,使用固定尺寸有助于提高性能。对于较大的尺寸,或在编译时尺寸不确定的情况下,使用动态尺寸更为合适。例如:

Matrix4f mymatrix;  // 固定尺寸矩阵
MatrixXf mymatrix(rows, columns);  // 动态尺寸矩阵
9、可选模版参数

其实Matrix 类一共有六个模版参数,前三个参数是必选的,也是最常用,后面三个模版参数是可选的。Matrix 类的完整模板参数列表如下:

Matrix<typename Scalar,
       int RowsAtCompileTime,
       int ColsAtCompileTime,
       int Options = 0,
       int MaxRowsAtCompileTime = RowsAtCompileTime,
       int MaxColsAtCompileTime = ColsAtCompileTime>

Options 是一个位字段,例如 RowMajor 表示行优先存储默认情况下,矩阵采用列优先存储。例如,以下定义表示行优先的 3x3 矩阵:

Matrix<float, 3, 3, RowMajor>

MaxRowsAtCompileTimeMaxColsAtCompileTime 用于指定矩阵的最大尺寸,可以避免动态内存分配。例如:

Matrix<float, Dynamic, Dynamic, 0, 3, 4>
10、便捷类型定义

Eigen 定义了以下便捷类型:

  • MatrixNt 表示 Matrix<type, N, N>,例如 MatrixXi 表示 Matrix<int, Dynamic, Dynamic>
  • VectorNt 表示 Matrix<type, N, 1>,例如 Vector2f 表示 Matrix<float, 2, 1>
  • RowVectorNt 表示 Matrix<type, 1, N>,例如 RowVector3d 表示 Matrix<double, 1, 3>

这些类型定义使得使用 Eigen 更加方便和直观。

二、使用Eigen进行矩阵和向量运算

Eigen提供了一个全面的运算库,用于矩阵和向量的算术操作,使得矩阵和向量的操作和计算变得直观和简单。以下是基本操作的概述,包括加法、减法、标量乘法、转置、矩阵乘法和基本的归约操作。

1、加法和减法

矩阵和向量的加法与减法要求左右操作数具有相同的行数和列数,并且必须具有相同的标量类型,因为Eigen不会自动进行类型提升。可用的运算符包括:

  • 二元运算符 +(如 a + b
  • 二元运算符 -(如 a - b
  • 一元运算符 -(如 -a
  • 复合运算符 +=(如 a += b
  • 复合运算符 -=(如 a -= b
#include <iostream>
#include <Eigen/Dense>

int main()
{
  Eigen::Matrix2d a;
  a << 1, 2,
       3, 4;
  Eigen::MatrixXd b(2, 2);
  b << 2, 3,
       1, 4;
  std::cout << "a + b =\n" << a + b << std::endl;
  std::cout << "a - b =\n" << a - b << std::endl;
  std::cout << "执行 a += b;" << std::endl;
  a += b;
  std::cout << "现在 a =\n" << a << std::endl;
  Eigen::Vector3d v(1, 2, 3);
  Eigen::Vector3d w(1, 0, 0);
  std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
}

输出:

a + b =
3 5
4 8
a - b =
-1 -1
 2  0
执行 a += b;
现在 a =
3 5
4 8
-v + w - v =
-1
-4
-6
2、标量乘法和除法

矩阵和向量的标量乘法和除法也非常简单。可用的运算符包括:

  • 二元运算符 *(如 matrix * scalarscalar * matrix
  • 二元运算符 /(如 matrix / scalar
  • 复合运算符 *=(如 matrix *= scalar
  • 复合运算符 /=(如 matrix /= scalar

示例:

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

int main()
{
  Eigen::Matrix2d a;
  a << 1, 2,
       3, 4;
  Eigen::Vector3d v(1, 2, 3);
  std::cout << "a * 2.5 =\n" << a * 2.5 << std::endl;
  std::cout << "0.1 * v =\n" << 0.1 * v << std::endl;
  std::cout << "执行 v *= 2;" << std::endl;
  v *= 2;
  std::cout << "现在 v =\n" << v << std::endl;
}

输出:

a * 2.5 =
2.5   5
7.5  10
0.1 * v =
0.1
0.2
0.3
执行 v *= 2;
现在 v =
2
4
6
3、转置和共轭

矩阵或向量的转置(a.transpose())、共轭(a.conjugate())和伴随矩阵(即共轭转置,a.adjoint())通过成员函数分别获取。

示例:

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

int main()
{
  Eigen::MatrixXcf a = Eigen::MatrixXcf::Random(2, 2);
  std::cout << "这是矩阵 a\n" << a << std::endl;

  std::cout << "这是矩阵 a^T\n" << a.transpose() << std::endl;

  std::cout << "这是 a 的共轭\n" << a.conjugate() << std::endl;

  std::cout << "这是矩阵 a^*\n" << a.adjoint() << std::endl;
}

输出:

这是矩阵 a
(-0.211,0.68) (-0.605,0.823)
 (0.597,0.566) (0.536,-0.33)
这是矩阵 a^T
(-0.211,0.68)  (0.597,0.566)
(-0.605,0.823) (0.536,-0.33)
这是 a 的共轭
(-0.211,-0.68) (-0.605,-0.823)
 (0.597,-0.566) (0.536,0.33)
这是矩阵 a^*
(-0.211,-0.68) (0.597,-0.566)
(-0.605,-0.823) (0.536,0.33)

对于实数矩阵,共轭操作是无效的,因此 adjoint() 等同于 transpose()

需要注意的是,transpose()adjoint() 返回一个代理对象而不进行实际转置。如果执行           a = a.transpose();Eigen会在转置操作完成之前开始写入 a,导致意外结果。解决方法是使用 transposeInPlace() 进行就地转置

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

int main()
{
  Eigen::MatrixXf a(2, 3);
  a << 1, 2, 3,
       4, 5, 6;
  std::cout << "这是初始矩阵 a:\n" << a << std::endl;

  a.transposeInPlace();
  std::cout << "转置后的矩阵 a:\n" << a << std::endl;
}

输出:

这是初始矩阵 a:
1 2 3
4 5 6
转置后的矩阵 a:
1 4
2 5
3 6
4、矩阵乘法和矩阵向量乘法

矩阵与矩阵的乘法、矩阵与向量的乘法通过运算符 * 实现。向量实际上是矩阵的一种特殊形式,因此矩阵向量乘法是矩阵乘法的一个特例。

示例:

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

int main()
{
  Eigen::Matrix2d mat;
  mat << 1, 2,
         3, 4;
  Eigen::Vector2d u(-1, 1), v(2, 0);
  std::cout << "这是 mat*mat:\n" << mat * mat << std::endl;
  std::cout << "这是 mat*u:\n" << mat * u << std::endl;
  std::cout << "这是 u^T*mat:\n" << u.transpose() * mat << std::endl;
  std::cout << "这是 u^T*v:\n" << u.transpose() * v << std::endl;
  std::cout << "这是 u*v^T:\n" << u * v.transpose() << std::endl;
  std::cout << "让我们将 mat 自乘" << std::endl;
  mat = mat * mat;
  std::cout << "现在 mat 是:\n" << mat << std::endl;
}

输出:

这是 mat*mat:
 7 10
15 22
这是 mat*u:
1
1
这是 u^T*mat:
2 2
这是 u^T*v:
-2
这是 u*v^T:
-2 0
 2 0
让我们将 mat 自乘
现在 mat 是:
 7 10
15 22
5、点积和叉积

对于点积和叉积,需要使用 dot() cross() 方法。当然,点积也可以通过 u.adjoint() * v 得到。

示例:

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

int main()
{
  Eigen::Vector3d v(1, 2, 3);
  Eigen::Vector3d w(0, 1, 2);

  std::cout << "点积: " << v.dot(w) << std::endl;
  double dp = v.adjoint() * w; // 自动将内积转换为标量
  std::cout << "通过矩阵乘积获得的点积: " << dp << std::endl;
  std::cout << "叉积:\n" << v.cross(w) << std::endl;
}

输出:

点积: 8
通过矩阵乘积获得的点积: 8
叉积:
 1
-2
 1

注意,叉积仅适用于大小为3的向量。点积适用于任意大小的向量。对于复数,Eigen的点积在第一个变量上是共轭线性的,在第二个变量上是线性的。

6、基本算数归约操作

Eigen还提供了一些归约操作,将给定的矩阵或向量归约为单个值,如和(通过 sum() <

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值