博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Eigen学习笔记
阅读量:2134 次
发布时间:2019-04-30

本文共 3093 字,大约阅读时间需要 10 分钟。

最近由于课题原因,新接触到一个C++的线性代数模板库,名为Eigen(读作 ['aɪgən])。主页位于

简单理解Eigen就是对矩阵和向量进行了抽象和建模,并且设计了相关的线性代运算的实现。简单尝试了之后发现上手很快,但是某些功能由于设计理念的独特性,导致最终的实现方案比较特殊。

简单看了一下Eigen的在线文档,记录几点用法。

系统是Ubuntu 16.04。

1 安装

非常特殊的是,作为C++库,Eigen不需要编译,而是以头文件的形式提供给用户的,之前用过的库貌似tinyXML也是这样。所以,所谓的安装在Linux上就是向/usr/local/include做了一个文件夹的符号链接。既创建符号链接在/usr/local/include/Eigen指向Eigen的解压后的位置。

2 初步测试

直接抄官方文档中getting start的示例程序,测试了一下矩阵的创建,非常直接非常简单。

#include 
#include
using Eigen::MatrixXd;int main(void){ 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 << m << std::endl; return 0;}
其中MatrixXd代表是运行时指定大小的矩阵(大写的X),使用双精度浮点数存储矩阵元素(最后的小写d)。可以看到,圆括号有重载。流输出也有重载。

由于创建了系统级别的符号链接,所以编译时不需要任何特殊设定,g++即可查找到对应的头文件并且完成编译。程序执行的效果如下。

No big deal!

同样直接抄官方的示例代码

#include 
#include
using namespace Eigen;using namespace std;int main(void){ MatrixXd m = MatrixXd::Random(3, 3); m = (m + MatrixXd::Constant(3,3,1.2)) * 50; cout << "m = " << endl << m << endl; VectorXd v(3); v << 1, 2, 3; cout << "v = " << endl << v << endl; cout << "m * v = " << endl << m * v << endl; return 0;}
运行结果如下。

于是矩阵加法,数乘,矩阵与向量的乘法都有运算符重载,很不错,有种MATLAB的感觉。

出了MatrixXd类和VectorXd类以外,Eigen还定义了常用维度的矩阵。根据Eigen文档的说法,在编译时能够确定矩阵或向量维度时,可以给Eigen优化的空间,并提供更高的执行效率。

3 Map, Stride, Reshape, Slicing和Block操作

Eigen默认采用列主导(column major)的数据存储形式,这点和FORTRAN还有MATLAB是一样的,当然也可以选择采用行主导,但是我感觉真心没必要。我一开始比较关心的是Eigen是否定义了比较方便的额slicing操作。但是似乎Eigen的Slicing和我理解的不太一样,有点复杂。Eigen的文档中,Slicing和Reshape是一起讲的,原因在于他们都是利用Eigen定义的Map功能实现的。简单理解Map就是将原始“连续内存存储”的数据,以矩阵形式重新组织。在使用Map时就需要原始数据,Map后的数据的维度形式,Map时使用的Stride设定。所谓Stride,既指矩阵中沿着矩阵列或行方向移动一个位置,内存中需要移动的位置数。这个需要移动的内存位置数与矩阵采用的存储方式有关(列或行主导)。Eigen定义了常用的Stride,既innerStride和outerStride。最好理解以上两个stride的方法就是看实例。innerStride既表示沿着矩阵的数据存储方向移动一个元素的位置,在内存中需要移动的

宽度。outerStride的含义就是不沿着数据存储方向移动一个位置,在内存中需要移动的宽度。还是用实例进行说明。

#include 
#include
using namespace Eigen;using namespace std;int main(void){ MatrixXf M1 = MatrixXf::Random(3,8); cout << "Column major input:" << endl << M1 << "\n"; cout << "M1.outerStride() = " << M1.outerStride() << endl; cout << "M1.innerStride() = " << M1.innerStride() << endl; Map
<> > M2( M1.data(), M1.rows(), (M1.cols()+2)/3, OuterStride<>(M1.outerStride()*3)); cout << "1 column over 3:" << endl << M2 << "\n"; return 0;}
以上实例代码中,是在一个3x8的矩阵中,取出包括第1列在内的3列,每隔2列取1列。创建M2时的最后一个参数即为Stride类型的变量。在这个例子中,Map的Stride被定义为每隔一列将空过9个元素的存储位置。代码的执行效果如下图。

Reshape的操作与Slicing很像。

目测Slicing都必须从原始数据的最开始进行,想要原始矩阵中的某一列后者某一行,或者某一块,就需要Block操作。实际最经常用的就是取出一行或者一列的操作。设计了如下的实例代码。

#include 
#include
using namespace Eigen;using namespace std;int main(void){ MatrixXf M1 = MatrixXf::Random(3,8); cout << "Column major input:" << endl << M1 << "\n"; cout << "The first column is:" << endl << M1.col(0) << "\n"; cout << "The last column is: " << endl << M1.rightCols(1) << "\n"; cout << "The first row is: " << endl << M1.topRows<1>() << endl; cout << "The last row is: " << endl << M1.bottomRows<1>() << endl; return 0;}
在上面代码中,测试了col()函数等Eigen设计好用于获取某列某行,或者从开头或者结尾开始计算的连续多行或者多列的函数。执行结果如下图。

4 神奇的注意点

关于class内部声明Eigen对象的特殊问题。

你可能感兴趣的文章
分析C语言的声明
查看>>
TCP为什么是三次握手,为什么不是两次或者四次 && TCP四次挥手
查看>>
C结构体、C++结构体、C++类的区别
查看>>
进程和线程的概念、区别和联系
查看>>
CMake 入门实战
查看>>
绑定CPU逻辑核心的利器——taskset
查看>>
Linux下perf性能测试火焰图只显示函数地址不显示函数名的问题
查看>>
c结构体、c++结构体和c++类的区别以及错误纠正
查看>>
Linux下查看根目录各文件内存占用情况
查看>>
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
查看>>
利用栈实现DFS
查看>>
逆序对的数量(递归+归并思想)
查看>>
数的范围(二分查找上下界)
查看>>
算法导论阅读顺序
查看>>
Windows程序设计:直线绘制
查看>>
linux之CentOS下文件解压方式
查看>>
Django字段的创建并连接MYSQL
查看>>
div标签布局的使用
查看>>
HTML中表格的使用
查看>>
(模板 重要)Tarjan算法解决LCA问题(PAT 1151 LCA in a Binary Tree)
查看>>