C++版NumPy-Eigen库快速入门

2024-04-14 17:32
文章标签 c++ 入门 快速 numpy eigen

本文主要是介绍C++版NumPy-Eigen库快速入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Eigen库的使用

    • 零、前言
    • 一、Eigen矩阵类
      • 1、矩阵类模板参数
      • 2、向量
      • 3、特值动态
      • 4、矩阵和向量的初始化
      • 5、矩阵和向量的存取
      • 6、逗号初始化
      • 7、调整大小
    • 二、矩阵和向量算法
      • 1、加减
      • 2、标量乘法和除法
      • 3、转置、共轭和伴随
      • 4、矩阵矩阵和矩阵向量乘法
      • 5、点积和叉积
      • 6、基本算术归约运算
      • 7、操作的有效性
      • 三、数组类和按系数运算
      • 1、数组类
      • 2、访问数组内的值
      • 3、加减
      • 4、数组乘法
      • 5、其他系数运算
      • 6、在数组和矩阵表达式之间转换
    • 四、块操作
      • 1、使用块操作
      • 2、列和行
      • 3、与角相关的操作
      • 4、向量的块运算
    • 五、Eigen高级初始化
      • 1、逗号初始值设定项
      • 2、特殊矩阵和数组
      • 3、用作临时对象

零、前言

如果你是一个pythoner,一定知道NumPy操作维度数组和矩阵非常方便,我也在实际开发过程中使用过NumPy来进行过各种算术运算,真是大大地提高了工作效率;曾经一直羡慕pythoner能这么方便的使用NumPy进行各种维度和矩阵运算,直到遇到了Eigen库,它堪称C++版的NumPy,虽然在使用的过程中发现它和NumPy比还有一些不足,但已经很好了,希望它越来越强大。网上虽然已经有了很多有关Eigen的介绍使用,但是自己在使用过程中还是有很多疑惑,于是根据官方提供的文档整理而成这篇文章,希望能够帮助初学者快速入门。

一、Eigen矩阵类

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

1、矩阵类模板参数

该矩阵类需要六个模板参数,平时我们主要用到的就前三个参数,剩下的三个参数默认即可。

1>必选模板参数
Matrix的三个必需模板参数是:
Matrix <typename Scalar,int RowsAtCompileTime,int ColsAtCompileTime>
Scalar是标量类型,即系数的类型,如果要使用浮点数矩阵,这里就传入float。

RowsAtCompileTime和ColsAtCompileTime是在编译时已知的矩阵的行数和列数。
例如:Matrix4f是一个4x4的浮点矩阵,Eigen定义的方式:
typedef Matrix <float,4,4> Matrix4f;

2>可选模板参数
Matrix类其余三个参数是可选的,模板参数的完整列表:
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>

Options是位字段,用来指定是行优先存储还是列优先存储,默认情况下,矩阵和向量的存储方式是“列优先存储”,可以传值RowMajor和ColMajor。

例如,行优先的3x3矩阵:
<float,3、3,RowMajor>

MaxRowsAtCompileTime和MaxColsAtCompileTime用来指定矩阵大小的上限,即使在编译时不知道矩阵的确切大小,在编译时也知道固定的上限,这样做的最大原因是避免动态内存分配。

例如,下面矩阵类型使用12个浮点数的普通数组,而没有动态内存分配:
Matrix<float, Dynamic, Dynamic, 0, 3, 4>

2、向量

在Eigen中,向量只是具有1行或1列的矩阵的一种特殊情况,只有1列的向量称为列向量,只有1行的称为行向量。

例如,typedef Vector3f是3个浮点数的(列)向量。Eigen定义如下:
typedef Matrix <float,3,1> Vector3f;
行向量的定义:
typedef Matrix <int,1,2> RowVector2i;

3、特值动态

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

例如typedef MatrixXd定义为具有动态大小的双精度矩阵,其定义如下:
typedef Matrix <double,Dynamic,Dynamic> MatrixXd;

定义固定数量的行和动态的列数,例如:
矩阵<float,3,Dynamic>

4、矩阵和向量的初始化

默认构造函数始终可用,从不执行任何动态内存分配,并且从不初始化矩阵系数。可以这样定义:

Matrix3f a;
MatrixXf b;

这里:
a 是一个3×3矩阵,具有未初始化系数的普通float [9]数组,
b 是一个动态大小的矩阵,其大小当前为0 x 0,并且其系数数组尚未分配。
也可以提供采用大小的构造函数。对于矩阵,总是先传递行数。对于矢量,只需传递矢量大小即可。他们分配给定大小的系数数组,但不自行初始化系数:

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

这里:
a 是10x15动态尺寸矩阵,具有已分配但当前尚未初始化的系数。
b 是大小为30的动态大小向量,具有已分配但当前尚未初始化的系数。
为了在固定大小和动态大小的矩阵之间提供统一的API,合法的是在固定大小的矩阵上使用这些构造函数,即使在这种情况下传递大小都没有用。所以这是合法的:

Matrix3f a(3,3);

构造函数来初始化矢量的系数:

Vector2d a(5.06.0);
Vector3d b(5.06.07.0);
Vector4d c(5.06.07.08.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 << "Here is the matrix m:\n" << m << std::endl;  VectorXd v(2);  v(0) = 4;  v(1) = v(0) - 1;  std::cout << "Here is the vector v:\n" << v << std::endl;} 程序输出:Here is the matrix m:  3  -12.5 1.5 Here is the vector v:43

语法m(index)不限于矢量,它也可用于一般矩阵,这意味着在系数数组中基于索引的访问。但是,这取决于矩阵的存储顺序。所有Eigen矩阵默认为列优先存储顺序,但是可以将其更改为行优先。

6、逗号初始化

Eigen矩阵和矢量支持逗号初始化的语法:
例:

Matrix3f m;
m << 1, 2, 3,    4, 5, 6,    7, 8, 9;
std::cout << m; 
输出结果:
1 2 34 5 67 8 9

7、调整大小

矩阵的当前大小可以通过rows(),cols()和size()检索。这些方法分别返回行数,列数和系数数。调整动态大小矩阵的大小是通过resize()方法完成的。
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
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;  
VectorXd v(2);  v.resize(5);  
std::cout << "The vector v is of size " << v.size() << std::endl;  
std::cout << "As a matrix, v is of size "            << v.rows() << "x" << v.cols() << std::endl;
} 
输出结果:
The matrix m is of size 4x3
It has 12 coefficients
The vector v is of size 5
As a matrix, v is of size 5x1

如果实际矩阵大小不变,则resize()方法为空操作。否则具有破坏性:系数的值可能会更改。

为了API的统一性,所有这些方法仍然可以在固定大小的矩阵上使用。实际上不能调整固定大小的矩阵的大小。尝试将固定大小更改为实际不同的值将触发断言失败。但是以下代码是合法的:
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
Matrix4d m;  
m.resize(4,4); // no operation  
std::cout << "The matrix m is of size "<< m.rows() << "x" << m.cols() << std::endl;
} 
输出结果:
The matrix m is of size 4x4

分配和调整大小
赋值是使用将矩阵复制到另一个矩阵中的操作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; 
输出结果:
a is of size 2x2a is now of size 3x3

固定尺寸与动态尺寸
什么时候应该使用固定尺寸(例如Matrix4f),什么时候应该使用动态尺寸(例如MatrixXf)?
简单的答案是:将固定尺寸用于可以使用的非常小的尺寸,将动态尺寸用于较大的尺寸或必须使用的尺寸。对于小尺寸,特别是对于小于(大约)16的尺寸,使用固定尺寸对性能有极大的好处,因为它使Eigen避免动态内存分配。

在内部,固定大小的Eigen矩阵只是一个简单的数组,即
Matrix4f mymatrix;
等于float mymatrix[16];
MatrixXf对象还将其行数和列数存储为成员变量。

当然,使用固定大小的限制是,只有当您在编译时知道大小时,才有可能这样做。对于足够大的尺寸,例如,对于大于(大约)32的尺寸,使用固定尺寸的性能优势变得可以忽略不计。糟糕的是,尝试使用函数内部的固定大小创建非常大的矩阵可能会导致堆栈溢出,因为Eigen会尝试将数组自动分配为局部变量,而这通常是在堆栈上完成的。

二、矩阵和向量算法

Eigen通过常见的C ++算术运算符(例如+,-,*)的重载,或通过诸如dot(),cross()等特殊方法的方式提供矩阵/向量算术运算。对于Matrix类(矩阵和向量),运算符仅重载以支持线性代数运算。

1、加减

左侧和右侧必须具有相同数量的行和列,还必须具有相同的标量类型,因为Eigen不会自动进行类型升级。操作如下:
二进制运算符+如 a+b
二进制运算符-如 a-b
一元运算符-如 -a
复合运算符+ =如 a+=b
复合运算符-=如 a-=b
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen;int main()
{  
Matrix2d a; a << 1, 2,       3, 4;  
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;  
Vector3d v(1,2,3);  
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
Doing a += b;
Now a =
3 5
4 8
-v + w - v =
-1
-4
-6

2、标量乘法和除法

标量的乘法和除法也非常简单,操作如下:
二进制运算符如 matrixscalar
二进制运算符如 scalarmatrix
二元运算符/如 matrix/scalar
复合运算符* =如 matrix*=scalar
复合运算符/ =如 matrix/=scalar
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
Matrix2d a;  
a << 1, 2,       
3, 4;  
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 << <

这篇关于C++版NumPy-Eigen库快速入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/903575

相关文章

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

Python利用ElementTree实现快速解析XML文件

《Python利用ElementTree实现快速解析XML文件》ElementTree是Python标准库的一部分,而且是Python标准库中用于解析和操作XML数据的模块,下面小编就来和大家详细讲讲... 目录一、XML文件解析到底有多重要二、ElementTree快速入门1. 加载XML的两种方式2.

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元