[Eigen中文文档] 矩阵与向量运算
专栏总目录
本文目录
- 介绍
- 加法与减法
- 标量的标量乘法与除法
- 表达式模板
- 转置与共轭
- (矩阵与矩阵)和(矩阵与向量)的乘积
- 点积和叉积
- 基本算术的简化运算
- 操作的有效性
英文原文(Matrix and vector arithmetic)
本文章旨在提供有关如何使用 Eigen 在矩阵、向量和标量之间执行算术操作的概述和一些详细信息。
介绍
Eigen 通过重载常见的 C++ 算术运算符(如 +
、-
、*
)或通过特殊方法(如 dot()
、cross()
等)提供矩阵/向量算术运算。对于 Matrix 类(矩阵和向量),重载运算符仅支持线性代数运算。例如,matrix1 * matrix2
代表矩阵乘法,vector + scalar
向量与标量的加法是不合法的。如果想执行各种数组运算,而不是线性代数,请参阅 数组类与元素操作。
加法与减法
操作符左右两侧的矩阵必须有相同的行数和列数,且它们的元素必须是同种类型,因为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 << "Doing a += b;" << std::endl;a += b;std::cout << "Now 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 -12 0
Doing a += b;
Now a =
3 5
4 8
-v + w - v =
-1
-4
-6
标量的标量乘法与除法
标量的乘法和除法也非常简单。目前支持的运算符示例如下:
运算符 | 示例 | |
---|---|---|
二元运算符 | * |
matrix * scalar (矩阵 * 标量) |
二元运算符 | * |
scalar * 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 << "Doing v *= 2;" << std::endl;v *= 2;std::cout << "Now v =\n" << v << std::endl;
}
输出:
a * 2.5 =
2.5 5
7.5 10
0.1 * v =
0.1
0.2
0.3
Doing v *= 2;
Now v =
2
4
6
表达式模板
这是一个比较高级的话题,但在这里提出是比较有用的。在Eigen中,诸如+
之类的算术运算符,他们自己不执行任何操作,只是返回一个表达式对象,该对象描述了将要执行的计算操作。实际的计算发生在后面整个表达式被求值的时侯,比如使用=
运算符时。虽然这听起来很繁琐,但任何现代优化编译器都能优化掉这种抽象,从而得到完美优化代码。例如:
VectorXf a(50), b(50), c(50), d(50);
...
a = 3*b + 4*c + 5*d;
Eigen会把上述表达式编译成一个循环,这个数组只遍历一次。数组循环如下所示:
for(int i = 0; i < 50; ++i)a[i] = 3*b[i] + 4*c[i] + 5*d[i];
因此,你不要害怕使用相对较大的运算表达式,这只会给Eigen更多机会进行优化。
转置与共轭
矩阵或向量 a a a 的转置( a T a^T aT)、共轭( a ‾ \overline{a} a)和伴随( a ∗ a^* a∗ ,如共轭转置)可以分别通过函数transpose()
、 conjugate()
、adjoint()
求得。
示例如下:
MatrixXcf a = MatrixXcf::Random(2,2);
cout << "Here is the matrix a\n" << a << endl;cout << "Here is the matrix a^T\n" << a.transpose() << endl;cout << "Here is the conjugate of a\n" << a.conjugate() << endl;cout << "Here is the matrix a^*\n" << a.adjoint() << endl;
输出如下:
Here is the matrix a(-0.211,0.68) (-0.605,0.823)(0.597,0.566) (0.536,-0.33)
Here is the matrix a^T(-0.211,0.68) (0.597,0.566)
(-0.605,0.823) (0.536,-0.33)
Here is the conjugate of a(-0.211,-0.68) (-0.605,-0.823)(0.597,-0.566) (0.536,0.33)
Here is the matrix a^*(-0.211,-0.68) (0.597,-0.566)
(-0.605,-0.823) (0.536,0.33)
对于实数矩阵,共轭函数conjugate()
是空操作,所以共轭转置函数adjoint()
相当于转置transpose()
。
作为基本的操作运算,transpose()
和adjoint()
函数只返回一个代理对象而没有做任何操作。如果执行b = a.transpose()
,真正的转置计算是在写入b
的时候发生的。然而,这有一个复杂的问题,如果执行a = a.transpose()
,Eigen在转置计算完全完成之前就开始写入a,所以指令a = a.transpose()
不会得到预期的结果。
示例如下:
Matrix2i a; a << 1, 2, 3, 4;
cout << "Here is the matrix a:\n" << a << endl;a = a.transpose(); // !!! do NOT do this !!!
cout << "and the result of the aliasing effect:\n" << a << endl;
输出为:
Here is the matrix a:
1 2
3 4
and the result of the aliasing effect:
1 2
2 4
上述的问题就是所谓的混叠问题,在debug
模式下,当assertion
打开,这个问题可以自动检测到。(g++编译默认是debug模式,关闭需要使用-DNDEBUG
选项)。
对于就地转置,可以使用transposeInPlace()
函数:
示例如下:
MatrixXf a(2,3); a << 1, 2, 3, 4, 5, 6;
cout << "Here is the initial matrix a:\n" << a << endl;a.transposeInPlace();
cout << "and after being transposed:\n" << a << endl;
输出为:
Here is the initial matrix a:
1 2 3
4 5 6
and after being transposed:
1 4
2 5
3 6
同样,对于复杂矩阵的就地共轭也有adjointInPlace()
函数。
(矩阵与矩阵)和(矩阵与向量)的乘积
矩阵与矩阵间的乘积是通过运算符*
来完成的。由于向量是特殊的矩阵,所以向量和矩阵的乘积实际上只是矩阵与矩阵乘积的特例,向量与向量的外积也是如此。所有的情况都会被处理成两类:
运算符 | 示例 | |
---|---|---|
二元运算符 | * | a * b |
混合运算符 | *= |
a*=b (即a=a*b) |
示例如下:
#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 << "Here is mat*mat:\n" << mat*mat << std::endl;std::cout << "Here is mat*u:\n" << mat*u << std::endl;std::cout << "Here is u^T*mat:\n" << u.transpose()*mat << std::endl;std::cout << "Here is u^T*v:\n" << u.transpose()*v << std::endl;std::cout << "Here is u*v^T:\n" << u*v.transpose() << std::endl;std::cout << "Let's multiply mat by itself" << std::endl;mat = mat*mat;std::cout << "Now mat is mat:\n" << mat << std::endl;
}
输出为:
Here is mat*mat:7 10
15 22
Here is mat*u:
1
1
Here is u^T*mat:
2 2
Here is u^T*v:
-2
Here is u*v^T:
-2 -02 0
Let's multiply mat by itself
Now mat is mat:7 10
15 22
注意:如果你阅读过上面的关于表达式模板的段落并且担心 m = m * m
会引发混淆问题,这里请放心,Eigen把矩阵乘法作为一个特殊的例子,并在此引入了一个临时变量,所以它会编译为:
tmp = m*m;
m = tmp;
如果你知道你的矩阵乘法可以安全的计算并且没有混淆问题,那么你可以使用noalias()
函数来避免编译临时变量,例如:
c.noalias() += a * b;
更多细节请参考 aliasing
注意:对于担心性能的 BLAS 用户,表达式如:c.noalias() -= 2 * a.adjoint() * b;
可以完全的优化并触发一个类似矩阵乘法的函数调用。
点积和叉积
对于点积和叉积,需要使用 dot() 和 cross() 方法。当然,点积也可以像 u.adjoint()*v
一样得到一个1x1的矩阵。
示例如下:
#include <iostream>
#include <Eigen/Dense>int main()
{Eigen::Vector3d v(1,2,3);Eigen::Vector3d w(0,1,2);std::cout << "Dot product: " << v.dot(w) << std::endl;double dp = v.adjoint()*w; // automatic conversion of the inner product to a scalarstd::cout << "Dot product via a matrix product: " << dp << std::endl;std::cout << "Cross product:\n" << v.cross(w) << std::endl;
}
输出为:
Dot product: 8
Dot product via a matrix product: 8
Cross product:1
-21
注意,叉积仅适用于大小为 3
的向量。点积适用于任何大小的向量。使用复数时,Eigen的点积在第一个变量中是共轭线性的,在第二个变量中是线性的。
基本算术的简化运算
Eigen还提供了一些简单操作来将给定的矩阵或向量计算为标量,例如求和(sum())、乘积 ( prod() ) 、最大值 ( maxCoeff() ) 和最小值 ( minCoeff() ) 。
示例如下:
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::Matrix2d mat;mat << 1, 2,3, 4;cout << "Here is mat.sum(): " << mat.sum() << endl;cout << "Here is mat.prod(): " << mat.prod() << endl;cout << "Here is mat.mean(): " << mat.mean() << endl;cout << "Here is mat.minCoeff(): " << mat.minCoeff() << endl;cout << "Here is mat.maxCoeff(): " << mat.maxCoeff() << endl;cout << "Here is mat.trace(): " << mat.trace() << endl;
}
输出为:
Here is mat.sum(): 10
Here is mat.prod(): 24
Here is mat.mean(): 2.5
Here is mat.minCoeff(): 1
Here is mat.maxCoeff(): 4
Here is mat.trace(): 5
矩阵的迹(对角线系数的总和)可以通过函数trace()计算,也可以使用更高效的方法a.diagonal().sum()
。
也存在minCoeff
和maxCoeff
函数的变体,通过参数返回相应系数的坐标:
Matrix3f m = Matrix3f::Random();std::ptrdiff_t i, j;float minOfM = m.minCoeff(&i,&j);cout << "Here is the matrix m:\n" << m << endl;cout << "Its minimum coefficient (" << minOfM << ") is at position (" << i << "," << j << ")\n\n";RowVector4i v = RowVector4i::Random();int maxOfV = v.maxCoeff(&i);cout << "Here is the vector v: " << v << endl;cout << "Its maximum coefficient (" << maxOfV << ") is at position " << i << endl;
输出为:
Here is the matrix m:0.68 0.597 -0.33
-0.211 0.823 0.5360.566 -0.605 -0.444
Its minimum coefficient (-0.605) is at position (2,1)Here is the vector v: 1 0 3 -3
Its maximum coefficient (3) is at position 2
这里的输出,自己测试和我官网给出的有出入:
- 浮点型数字,官网截取了前面几位,而且做了四舍五入(前文已提到过)
- 整型数字,和官网完全不一样(暂未发现原因,如你知道欢迎留言)
关于这里的
Random()
函数,源码中有解释:Numbers are uniformly spread through their whole definition range for integer types, and in the [-1:1] range for floating point scalar types.
对于整型,在整个定义范围内均匀分布;对于浮点型,分布在[-1,1]的范围内。所以,从代码来看,官网的结果
Here is the vector v: 1 0 3 -3
是不符合的。!!!注意,以上两个问题,整篇都有,下文不再赘述!!!
操作的有效性
Eigen会检查操作的有效性,如果有错误,它会在编译的时候产生错误提示。这些错误提示可能又长又难看,但Eigen会把重要的信息写成大写,以使其更加显眼,例如:
Matrix3f m;
Vector4f v;
v = m*v; // Compile-time error: YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES
当然,在很多情况下,如检查动态矩阵的大小时,无法在编译时进行检查,Eigen会使用运行时的断言。这意味如果程序在debug模式下运行,遇到非法操作时会终止运行并打印出错误信息。如果关闭断言,程序可能会崩溃。
MatrixXf m(3,3);
VectorXf v(4);
v = m * v; // Run-time assertion failure here: "invalid matrix product"
[Eigen中文文档] 矩阵与向量运算相关推荐
- Eigen学习3:矩阵及向量运算
矩阵及向量运算 注意事项: Eigen中的矩阵和向量运算不会自动适应行列数,需要在编程的时候保证参与运算的矩阵和向量行列数可以进行运算; 头文件<Eigen/Core> 中包含[+,-,, ...
- [Eigen中文文档] 切片和索引
专栏总目录 本文目录 概述 基本的切片 编译时的大小和步长 倒序 索引序列 自定义索引列表 英文原文(Slicing and Indexing) 本文介绍了如何使用操作运算符operator()索引行 ...
- [Eigen中文文档] 稀疏矩阵快速参考指南
文档总目录 本文目录 稀疏矩阵初始化 构造 重置大小/预分配内存空间 赋值 逐元素插入 批量插入 指定位置或随机插入 矩阵属性 算术运算 加减 标量积 稀疏矩阵乘积 转置/伴随 排列 组件级操作 其他 ...
- [Eigen中文文档] 归约、访问者和广播
专栏总目录 本文目录 归约 范数计算 布尔归约 用户自定义的归约 访问者函数 局部归约 将局部归约与其他操作结合 广播 将广播与其他操作结合 英文原文(Reductions, visitors and ...
- [Eigen中文文档] 按值将Eigen对象传递给函数
文档总目录 本文目录 按值传递和按引用传递 Eigen的处理 英文原文(Passing Eigen objects by value to functions) 按值传递和按引用传递 按值传递:传入函 ...
- eigen之eigen中文文档
GitHub - growinguptogether/EigenDocInChinese: Eigen3.3.7 Doc中文版
- Eigen 学习文档: 矩阵和向量运算
Eigen 学习文档: 矩阵和向量运算 本页旨在提供有关如何使用Eigen在矩阵.向量和标量之间执行算术的概述和一些详细信息. 介绍 Eigen通过重载常见的 C++ 算术运算符(如 +.-.*)或通 ...
- 【Scikit-Learn 中文文档】数据集加载工具 - 用户指南 | ApacheCN
中文文档: http://sklearn.apachecn.org/cn/stable/datasets/index.html 英文文档: http://sklearn.apachecn.org/en ...
- Scikit-Learn 中文文档】数据集加载工具 - 用户指南 | ApacheCN
中文文档: http://sklearn.apachecn.org/cn/stable/datasets/index.html 英文文档: http://sklearn.apachecn.org/en ...
最新文章
- 重磅来袭!谷歌八年高级工程师亲授面试经验!!!
- ElasticSearch安装入门
- 计算机二级vf知识点总结,2015年计算机二级考试《VFP》复习重点:第四章
- C++中智能指针的设计和使用
- tomcat7 加载el表达式 报错 使用tomcat8得以解决
- UWP-HttpClient
- 基于lis3dh的简易倾角仪c源码_开源网关apisix源码阅读和最佳实践
- 无限复活服务器,绝地求生无限复活模式怎么玩 无限复活玩法说明介绍
- 传澳洲电讯急寻汽车之家买家,接手财团有意私有化
- Mysql中有哪些数据类型(建议收藏)
- 类火墙的iptables
- 迅雷 iOS 版终于复活,不限速,完美支持BT磁力下载
- axio请求获取网络数据
- 海内外弟子追思百岁国医大师邓铁涛
- bugzilla安装
- 计算机设备与驱动器空白图标,这个方法帮你删掉win10设备和驱动器里无效图标...
- 用Python做一个自动发送邮件的工具
- matlab画热力网格图
- [转贴]汉武帝太子刘据的悲剧
- 不经意传输协议-密码学