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实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

C++字符串提取和分割的多种方法

《C++字符串提取和分割的多种方法》在C++编程中,字符串处理是一个常见的任务,尤其是在需要从字符串中提取特定数据时,本文将详细探讨如何使用C++标准库中的工具来提取和分割字符串,并分析不同方法的适用... 目录1. 字符串提取的基本方法1.1 使用 std::istringstream 和 >> 操作符示

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑