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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多