[Eigen中文文档] 切片和索引

本文详细介绍了如何使用Eigen3.4中的DenseBase::operator()(constRowIndices&,constColIndices&)来索引和切片矩阵和向量的子集,包括使用seq和seqN函数进行等间距取值,以及使用符号如Eigen::all和Eigen::last进行动态索引。此外,还讨论了自定义索引列表和编译时大小与步长的应用,展示了如何通过自定义结构扩展矩阵的边界。
摘要由CSDN通过智能技术生成

专栏总目录

英文原文(Slicing and Indexing)

本文介绍了如何使用操作运算符operator()索引行和列的子集。该 API 在 Eigen 3.4 中引入。它支持 block API 提供的所有功能。特别是,它支持切片,即获取一组行、列或元素,以及等间隔的从矩阵或者数组中提取元素。

概述

所有上述操作都是通过DenseBase::operator()(const RowIndices&, const ColIndices&)来完成的,每一个参数可以是:

  • 索引单行或列的整数,包括符号索引
  • 符号Eigen::all表示按递增顺序排列的所有行或列
  • Eigen::seq, Eigen::seqN或者 Eigen::placeholders::lastN 函数构造的算数序列
  • 任意一维整数向量、数组,形式如Eigen 向量数组表达式std::vectorstd::array 、 C的数组int[N]

更一般的,该函数可以接受任何有下列两个成员函数接口的对象

<integral type> operator[](<integral type>) const;
<integral type> size() const;

其中<integral type> 代表任何可以与Eigen::index兼容的整数,如std::ptrdiff_t

基本的切片

通过Eigen::seqEigen::seqN函数,取矩阵或向量中均匀间隔的一组行、列或元素,其中seq代表等差数列,用法如下:

方法描述示例
seq(firstIdx,lastIdx)返回从firstIdxlastIdx的整数序列seq(2,5) <=> {2,3,4,5}
seq(firstIdx,lastIdx,incr)同上,但是索引步长为incrseq(2,8,2) <=> {2,4,6,8}
seqN(firstIdx,size)firstIdx开始,索引步长1,总的个数为sizeseqN(2,5) <=> {2,3,4,5,6}
seqN(firstIdx,size,incr)同上,索引步长为incrseqN(2,3,3) <=> {2,5,8}

一旦等差数列通过operator()传递给它,firstIdxlastIdx参数也可以用Eigen::last符号来定义,该符号表示矩阵/向量的最后一行、最后一列或最后一个元素的索引,使用如下:

目的代码等价的块操作
包含从第i行到最后一行,从第0列开始,共n列的块A(seq(i,last), seqN(0,n))A.bottomLeftCorner(A.rows()-i,n)
包含从第i行到第m行,从第j列开始,共n列的块A(seqN(i,m), seqN(j,n))A.block(i,j,m,n)
包含从第i0行到第i1行,从第j0列到第j1列的块A(seq(i0,i1), seq(j0,j1)A.block(i0,j0,i1-i0+1,j1-j0+1)
A的偶数列A(all, seq(0,last,2))
A的前n个奇数行A(seqN(1,n,2), all)
倒数第二列A(all, last-1)A.col(A.cols()-2)
A的中间一行A(last/2,all)A.row((A.rows()-1)/2)
向量v从第i个元素到最后一个元素v(seq(i,last))v.tail(v.size()-i)
向量v的最后n个元素v(seq(last+1-n,last))v.tail(n)

示例如下:

// 代码索引 3-5-1-1
MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"
     << A << "\n\n";

// 包含从第i行到最后一行,从第0列开始,共n列的块(i: 2, n: 3)
cout << "A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)\n"
     << A(seq(2, Eigen::last), seqN(0, 3)) << "\n\n";

// 包含从第i行到第m行,从第j列开始,共n列的块(i: 1, m: 2, j: 2, n: 4)
cout << "A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)\n"
     << A(seq(1, 2), seqN(2, 4)) << "\n\n";

// 包含从第i0行到第i1行,从第j0列到第j1列的块(i0: 1, i1: 2, j0: 2, j1: 4)
cout << "A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)\n"
     << A(seq(1, 2), seq(2, 4)) << "\n\n";

// A的偶数列
cout << "A的偶数列: \n"
     << A(Eigen::all, seq(0, Eigen::last, 2)) << "\n\n";

// A的前n个奇数行 (n: 3)
cout << "A的前n个奇数行: (n: 3)\n"
     << A(seqN(1, 3, 2), Eigen::all) << "\n\n";

// A的倒数第二列
cout << "A的倒数第二列: \n"
     << A(Eigen::all, Eigen::last-1) << "\n\n";

// A的中间一行
cout << "A的中间一行: \n"
     << A(Eigen::last/2, Eigen::all) << "\n\n";


Eigen::VectorXi v{{4,2,5,8,3}};
cout << "Initial vector v:\n"
     << v << "\n\n";

// 向量v从第i个元素到最后一个元素 (i: 2)
cout << "向量v从第i个元素到最后一个元素: (i: 2)\n"
     << v(seq(2, Eigen::last)) << "\n\n";

// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"
     << v(seq(Eigen::last-3+1, Eigen::last)) << "\n\n";

输出如下:

Initial matrix A:
  730547559   576018668   971155939  -552146456  1071432243    52156343
 -226810938  -477225175   893772102  -779039257  -605038689   -13780431
  607950953   115899597   291438716   653214605    27772105  1015276632
  640895091   -48539462   466641602  -737276042   728237978  -445566813
  884005969   276748203  -769652652  -212720294   241892198   582736218
 -649503489  -290373134   229713912  -795018962  -438018766    57434405
 -353856438    28778235 -1038736613  -840076701   295391245   579635549

A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)
  607950953   115899597   291438716
  640895091   -48539462   466641602
  884005969   276748203  -769652652
 -649503489  -290373134   229713912
 -353856438    28778235 -1038736613

A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)
 893772102 -779039257 -605038689  -13780431
 291438716  653214605   27772105 1015276632

A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)
 893772102 -779039257 -605038689
 291438716  653214605   27772105

A的偶数列: 
  730547559   971155939  1071432243
 -226810938   893772102  -605038689
  607950953   291438716    27772105
  640895091   466641602   728237978
  884005969  -769652652   241892198
 -649503489   229713912  -438018766
 -353856438 -1038736613   295391245

A的前n个奇数行: (n: 3)
-226810938 -477225175  893772102 -779039257 -605038689  -13780431
 640895091  -48539462  466641602 -737276042  728237978 -445566813
-649503489 -290373134  229713912 -795018962 -438018766   57434405

A的倒数第二列: 
1071432243
-605038689
  27772105
 728237978
 241892198
-438018766
 295391245

A的中间一行: 
 640895091  -48539462  466641602 -737276042  728237978 -445566813

Initial vector v:
4
2
5
8
3

向量v从第i个元素到最后一个元素: (i: 2)
5
8
3

向量v最后n个元素: (n: 3)
5
8
3

正如在上一个示例中看到的,引用最后n个元素(或行/列)编写起来有点麻烦。使用非默认增量时,这将变得更加棘手和容易出错。因此,Eigen提供了Eigen::placeholders::lastN(size)Eigen::placeholders::lastN(size,incr)函数来完成最后几个元素的提取,用法如下:

Eigen官方建议使用Eigen::lastN(size)Eigen::lastN(size,incr)代替Eigen::placeholders::lastN(size)Eigen::placeholders::lastN(size,incr)

目的代码等价的块操作
向量v的最后n个元素v(lastN(n))v.tail(n)
A右下角m行n列的块A(lastN(m), lastN(n))A.bottomRightCorner(m,n)
A的最后n列,步长为mA(all, lastN(n,m))

示例如下:

Eigen::VectorXi v{{4,2,5,8,3}};
cout << "Initial vector v:\n"
     << v << "\n\n";
     
// Eigen提供最后几个元素的提取函数
// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"
     << v(Eigen::lastN(3)) << "\n\n";
     
MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"
     << A << "\n\n";
     
// A右下角m行n列的块
cout << "A右下角m行n列的块: (m: 3, n: 2)\n"
     << A(Eigen::lastN(3), Eigen::lastN(2)) << "\n\n";

// A的最后n列,步长为m
cout << "A的最后n列, 步长为m: (n: 3, m: 2)\n"
     << A(Eigen::all, Eigen::lastN(3, 2)) << "\n\n";

输出如下:

Initial vector v:
4
2
5
8
3

向量v最后n个元素: (n: 3)
5
8
3

Initial matrix A:
  730547559   576018668   971155939  -552146456  1071432243    52156343
 -226810938  -477225175   893772102  -779039257  -605038689   -13780431
  607950953   115899597   291438716   653214605    27772105  1015276632
  640895091   -48539462   466641602  -737276042   728237978  -445566813
  884005969   276748203  -769652652  -212720294   241892198   582736218
 -649503489  -290373134   229713912  -795018962  -438018766    57434405
 -353856438    28778235 -1038736613  -840076701   295391245   579635549

A右下角m行n列的块: (m: 3, n: 2)
 241892198  582736218
-438018766   57434405
 295391245  579635549

A的最后n列, 步长为m: (n: 3, m: 2)
 576018668 -552146456   52156343
-477225175 -779039257  -13780431
 115899597  653214605 1015276632
 -48539462 -737276042 -445566813
 276748203 -212720294  582736218
-290373134 -795018962   57434405
  28778235 -840076701  579635549

编译时的大小和步长

在性能方面,Eigen和编译器可以利用编译时的大小和步长。为此,可以使用Eigen::fix<val>在编译时强制指定大小。而且,它可以和Eigen::last符号一起使用:

v(seq(last-fix<7>, last-fix<2>))

在这个示例中,Eigen在编译时就知道返回的表达式有6个元素。它等价于:

v(seqN(last-7, fix<6>))

我们可以访问A的偶数列,如:

A(all, seq(0,last,fix<2>))

倒序

也可以把步长设置为负数,按降序枚举行/列索引,例如,从第 20 列开始到第 10 列结束, 步长为-2

A(all, seq(20, 10, fix<-2>))

从最后一行开始,取n行:

A(seqN(last, n, fix<-1>), all)

也可以使用ArithmeticSequence::reverse() 方法来反转序列,前面的例子也可以写成:

A(lastN(n).reverse(), all)

索引序列

operator()输入的也可以是ArrayXi, std::vector<int>, std::array<int,N>等,如:

示例:

std::vector<int> ind{4,2,5,5,3};
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind):\n" << A(Eigen::placeholders::all,ind) << "\n\n";

输出如下:

Initial matrix A:
  7   9  -5  -3   3 -10
 -2  -6   1   0   5  -5
  6  -3   0   9  -8  -8
  6   6   3   9   2   6

A(all,ind):
  3  -5 -10 -10  -3
  5   1  -5  -5   0
 -8   0  -8  -8   9
  2   3   6   6   9

也可以直接传递一个静态数组:

MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,{4,2,5,5,3}):\n" << A(Eigen::placeholders::all,{4,2,5,5,3}) << "\n\n";

输出:

Initial matrix A:
  7   9  -5  -3   3 -10
 -2  -6   1   0   5  -5
  6  -3   0   9  -8  -8
  6   6   3   9   2   6

A(all,{4,2,5,5,3}):
  3  -5 -10 -10  -3
  5   1  -5  -5   0
 -8   0  -8  -8   9
  2   3   6   6   9

也可以传递一个表达式:

ArrayXi ind(5); ind<<4,2,5,5,3;
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind-1):\n" << A(Eigen::placeholders::all,ind-1) << "\n\n";

输出:

Initial matrix A:
  7   9  -5  -3   3 -10
 -2  -6   1   0   5  -5
  6  -3   0   9  -8  -8
  6   6   3   9   2   6

A(all,ind-1):
-3  9  3  3 -5
 0 -6  5  5  1
 9 -3 -8 -8  0
 9  6  2  2  3

当传递一个具有编译时大小的对象(如Array4istd::array<int, N>或静态数组)时,返回的表达式也会显示编译时维度。

自定义索引列表

更一般的,operator()可以接受任何类型的对象:

Index s = ind.size(); or Index s = size(ind);
Index i;
i = ind[i];

这意味着可以构建自己的序列生成器并将其传递给operator()。下面是一个通过重复填充额外的第一行和列来扩大给定矩阵的示例:

struct pad {
  Index size() const { return out_size; }
  Index operator[] (Index i) const { return std::max<Index>(0,i-(out_size-in_size)); }
  Index in_size, out_size;
};
 
Matrix3i A;
A.reshaped() = VectorXi::LinSpaced(9,1,9);
cout << "Initial matrix A:\n" << A << "\n\n";
MatrixXi B(5,5);
B = A(pad{3,5}, pad{3,5});
cout << "A(pad{3,N}, pad{3,N}):\n" << B << "\n\n";

输出:

Initial matrix A:
1 4 7
2 5 8
3 6 9

A(pad{3,N}, pad{3,N}):
1 1 1 4 7
1 1 1 4 7
1 1 1 4 7
2 2 2 5 8
3 3 3 6 9
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万俟淋曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值