您的位置:首页 > 编程语言 > C语言/C++

【1.1】Eigen C++ 矩阵开源库学习之稠密矩阵和数组操作——矩阵类

2018-01-20 15:52 429 查看
稠密矩阵和数组操作 http://eigen.tuxfamily.org/dox-devel/group__DenseMatrixManipulation__chapter.html

包含模块:

1.矩阵类

2.矩阵和向量的运算

3.数组操作

4.块操作

5.高级的初始化

6.减少,访客和广播

7.与原始缓冲区接口:Map类

8.重塑和切片

9.别名

10.存储方式

11.对齐问题

12.参考

13.系数智能函数的目录

14.快速参考指南

矩阵类

在Eigen中,所有的矩阵和向量都是Matrix模板类的对象。向量只是矩阵的一个特例,有1行或1列。

1.Matrix的前三个模板参数

Matrix矩阵类需要六个模板参数,但现在主需要掌握前三个参数即可。剩下的三个参数都有默认值。

Matrix的三个强制模板参数是:

Matrix < typename Scalar,int RowsAtCompileTime,int ColsAtCompileTime>


Scalar是标量类型,即系数的类型。也就是说,如果你想要一个浮动矩阵,请在这里选择float。(有关所有支持的标量类型的列表以及如何将支持扩展到新类型的信息,请参阅标量类型http://eigen.tuxfamily.org/dox-devel/TopicScalarTypes.html)。

RowsAtCompileTimeColsAtCompileTime是编译时已知的矩阵的行数和列数(如果在编译时不知道该数字,请参阅下面的内容)。

我们提供了很多方便的typedef来覆盖常见的情况。

例如,Matrix4f是一个4x4的浮点矩阵。Eigen定义如下:

typedef Matrix <float,4,4> Matrix4f ;


我们在下面讨论这些便利的typedef。

2.矢量

如上所述,在Eigen中,向量只是矩阵的一个特例,有1行或1列

他们有1列的情况是最常见的; 这样的向量被称为列向量,通常简称为向量。

在另一个有1行的情况下,它们被称为行向量

例如,便捷typedef Vector3f是3个浮点数的(列)向量。Eigen定义如下:

typedef Matrix <float,3,1> Vector3f ;


我们还为行向量提供便利的typedef,例如:

typedef Matrix <int,1,2> RowVector2i ;


3.特殊值Dynamic

当然,Eigen不限于在编译时已知尺寸的矩阵。在RowsAtCompileTime和ColsAtCompileTime模板参数可以采取特殊值Dynamic这表明大小在编译时是未知的,所以必须作为运行时变量来处理。

在特征术语中,这样的尺寸被称为动态尺寸 ; 而编译时已知的大小称为固定大小

例如,typedef MatrixXd,意思是动态大小的double矩阵,定义如下:

typedef Matrix <double,Dynamic,Dynamic> MatrixXd ;


同样,我们定义一个不言自明的typedef VectorXi如下:

typedef Matrix <int,Dynamic,1> VectorXi ;


您可以完美地拥有固定行数,动态列数的矩阵,如下所示:

Matrix <float,3,Dynamic>


4.构造函数

不带参的构造函数(默认),不会执行任何动态的内存分配,也不会初始化矩阵系数。你可以这样定义:

Matrix3f a;
MatrixXf b;


这里,

- a 是一个3乘3的矩阵,具有9个未初始化系数的普通浮点数阵列。

- b 是一个动态大小的矩阵,其大小当前为0,系数阵列的大小没有被分配。

带参的构造函数

对于矩阵,行在前,列在后。

对于向量,只需传递向量的大小。

他们分配具有给定大小的系数阵列,但不会初始化系数本身:

MatrixXf a(10,15);
VectorXf b(30);


这里,

- a 是一个10x15的动态矩阵,具有已分配大小但目前未初始化的系数。

- b 是一个大小为30的动态大小的向量,具有已分配但目前未初始化的系数。

为了给固定大小和动态大小的矩阵中提供统一的API,在固定大小的矩阵上使用这些构造函数也是合法的,即使在这种情况下通过大小是无效的。以下是合法的,虽然是个空操作:

Matrix3f a(3,3);


最后,我们还提供了一些构造函数来初始化尺寸为4的小型固定尺寸向量的系数:

Vector2d a(5.0,6.0);
Vector3d b(5.0,6.0,7.0);
Vector4d c(5.0,6.0,7.0,8.0);


5.参数访问

Eigen中的主要参数访问和存储的方式是重载的 ==括号操作符==

对于矩阵,行索引总是先传递。对于向量,只需传递一个索引。编号从0开始。这个例子是不言自明的:

#include <iostream>
#include <Eigen/Dense>
using namespace Eigen ;
int main()
{
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:\ n” << m << std :: endl;
VectorXd v(2);
v(0)= 4;
v(1)= v(0)-1;
std :: cout << “向量v:\ n” << v << std :: endl;
}

/*
output:
矩阵m:
3   -1
2.5  1.5
向量v:
4
3
*/


请注意,语法 m(index) 不限于向量,它也可用于一般矩阵,这意味着在系数阵列中基于索引的访问。这取决于矩阵的存储顺序。所有特征矩阵默认为列主存储顺序,但可以将其更改为行主,请参阅存储顺序(http://eigen.tuxfamily.org/dox-devel/group__TopicStorageOrders.html)。

vector[]也被重载,用于向量中的基于索引的访问,但请记住,C++不允许operator []使用多个参数。所以我们限制运算符[]作用于矢量而不用于矩阵,因为C ++语言会尴尬地将将矩阵[i,j]编译为与矩阵[j]相同的东西!

6.逗号初始化

矩阵和向量系数可以使用 ==逗号初始化== 语法方便地设置。现在,知道这个例子就足够了:

Matrix3f m;     //定义一个3*3矩阵,使用逗号初始化.
m << 1,2,3,
4,5,6,
7,8,9;
std :: cout << m;
/*
输出:
1 2 3
4 5 6
7 8 9
*/


逗号隔开的数值还可以通过数学表达式来表示。

另可参考高级的初始化方式:http://eigen.tuxfamily.org/dox-devel/group__TutorialAdvancedInitialization.html

7.调整矩阵大小

矩阵的当前大小可以通过rows(),cols()和size()来检索

这些方法分别返回行数,列数和系数个数

==调整动态大小矩阵的大小由resize()== 方法完成。

#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
MatrixXd m(2,5);      //矩阵m原来的大小为2*5
m.resize(4,3);        //通过resize函数将大小改为4*3
std::cout << "矩阵m的大小为:"
<< m.rows() << "x" << m.cols() << std::endl;
std::cout << "拥有" << m.size() << "个系数" << std::endl;
VectorXd v(2);
v.resize(5);
std::cout << "向量v的大小为:" << v.size() << std::endl;
std::cout << "按照矩阵来看,v的大小为: "
<< v.rows() << "x" << v.cols() << std::endl;
}
/*
output:
矩阵m的大小是4×3
它有12个系数
矢量v的大小为5
按照矩阵来看,v的大小是5x1
*/


如果实际的矩阵大小没有改变,resize()方法是无操作的; 否则是破坏性的:系数的值可能会改变。如果你想要一个不改变系数的resize()的保守方法,使用conservativeResize(),更多细节见本页http://eigen.tuxfamily.org/dox-devel/TopicResizing.html

所有这些方法仍然可用于固定尺寸的矩阵,为了API的一致性。当然,你实际上不能调整固定大小的矩阵。尝试将固定大小更改为实际不同的值将触发断言失败; 但下面的代码是合法的:

#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix4d m;
m.resize(4,4); // 无效,但是合法
std::cout << "矩阵m的大小为: "
<< m.rows() << "x" << m.cols() << std::endl;
}

//output : 矩阵m的大小是4×4


7.分配和调整大小

赋值操作operator=是将矩阵复制到另一个矩阵中的。Eigen ==自动调整左侧矩阵的大小==,使其与右侧矩阵的大小相匹配。

MatrixXf a(2,2);
std::cout << "a is of size " << a.rows() << "x" << a.cols() << std::endl;
MatrixXf b(3,3);
a = b;
std::cout << "a is now of size " << a.rows() << "x" << a.cols() << std::endl


当然,==如果左侧是固定大小(如Matrix4f),则不允许调整大小。==

如果您不希望自动调整大小(例如出于调试目的),则可以禁用它,请参阅此页面(http://eigen.tuxfamily.org/dox-devel/TopicResizing.html)。

8.固定大小与动态大小的选择

什么时候应该使用固定大小(例如Matrix4f),什么时候更适合动态大小(例如MatrixXf)

简单的答案是:在允许的情况下使用固定的尺寸来实现非常小的尺寸,而对于更大的尺寸或者必须使用动态的尺寸则用动态大小

对于小尺寸,特别是对于尺寸小于(或等于)16的尺寸,使用固定尺寸对性能非常有利,因为它允许Eigen避免动态内存分配并展开循环

在内部,一个固定大小的特征矩阵只是一个普通的数组,也就是

Matrix4f mymatrix;


相当于

float mymatrix [16];


所以这确实具有零运行成本。

相比之下,动态大小矩阵的数组总是分配在堆上

MatrixXf mymatrix(rows, columns);


相当于做

float * mymatrix = new  float [rows * columns];


除此之外,MatrixXf对象将其行数和列数存储为成员变量。

当然,使用固定大小的限制是,只有在编译时知道大小的情况下才可以。而且,对于尺寸大于(大致)32的足够大的尺寸而言,使用固定尺寸的性能益处变得可以忽略不计。更糟糕的是,试图在函数中使用固定大小创建一个非常大的矩阵可能会导致堆栈溢出,因为Eigen会尝试自动分配数组作为局部变量,而这通常是在堆栈中完成的。

最后,根据情况,当使用动态大小时,Eigen也可以更积极地尝试矢量化(使用SIMD指令),参见矢量化(http://eigen.tuxfamily.org/dox-devel/TopicVectorization.html)。

9.可选的模板参数

我们在本页开头提到,Matrix类有六个模板参数,但到目前为止我们只讨论了前三个。其余三个参数是可选的。以下是模板参数的完整列表:

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


Options是一个位域。在这里,我们只讨论一点:RowMajor。它指定这种类型的矩阵使用行主存储顺序; 默认情况下,存储顺序是以列为主。查看前面给出的 ==存储方式选择==。例如,这种类型意味着以行为猪的主3×3矩阵:

Matrix <float,3,3,RowMajor>


MaxRowsAtCompileTime和MaxColsAtCompileTime也可以指定,即使矩阵的确切大小不知道,在编译时可以自己==制定上限==。这样做的最大原因是为了避免动态内存分配。例如,下面的矩阵类型使用一个12浮点数的普通数组,而没有动态内存分配:

Matrix <float,Dynamic,Dynamic,0,3,4>


10.便捷的typedefs

Eigen定义了以下Matrix typedef:

MatrixNt等价于Matrix < type,N,N>。例如,MatrixXi可表示Matrix < int,Dynamic,Dynamic>。

VectorNt为矩阵< type,N,1>。例如,Vector2f可表示Matrix < float,2,1>,。

RowVectorNt用于Matrix < type,1,N>。例如,RowVector3d可表示Matrix < double,1,3>。

其中:

- N可以是任何一个2,3,4,或X(意思Dynamic)。

- t可以是i(意思是int),f(意思是float),d(意思是double),cf(意思是复杂的)或cd(意思是复杂的)中的任何一个。

typedef只为这五种类型定义的事实并不意味着它们是唯一支持的标量类型。例如,支持所有标准整数类型,请参阅标量类型(http://eigen.tuxfamily.org/dox-devel/TopicScalarTypes.html)。

2018.01.20
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数学 SLAM Eigen