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

相关文章

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页