8.1.1高级索引

链式索引

Octave允许使用重复(链式)索引表达式在单个命令中提取数组的子集,而无需使用中间变量。这可以使编写具有复杂索引操作或使用多种索引方法的代码变得更容易。以下示例显示了两个等效的索引提取操作:

A = reshape (1:16, 4, 4);
B = A(2:4, 2:3);
C = B(3:5);
D = C( [ true, false, true ] )
     ⇒ D = [ 8, 11 ]

D = A(2:4, 2:3)(3:5)([ true, false, true ])
     ⇒ D = [ 8, 11 ]

链式索引必然比返回相同结果的单个索引表达式慢,但通常比使用中间变量赋值执行多个离散索引操作更具计算效率。

请注意,链式索引仅与右值运算兼容,不能用于左值运算。

分量到线性索引的转换

当需要从数组中提取条目的子集时,如果这些条目的子集不能写成分量的笛卡尔积,则可以使用sub2ind与函数一起进行线性索引。例如:

A = reshape (1:8, 2, 2, 2)  # Create 3-D array
A =

ans(:,:,1) =

   1   3
   2   4

ans(:,:,2) =

   5   7
   6   8

A(sub2ind (size (A), [1, 2, 1], [1, 1, 2], [1, 2, 1]))
   ⇒ ans = [A(1, 1, 1), A(2, 1, 2), A(1, 2, 1)]
 
: ind= sub2ind (dims, i, j)
: ind= sub2ind (dims, s1, s2, …, sN)

将下标转换为线性索引。

输入dims是一个维度向量,其中每个元素都是相应维度中数组的大小(详见size). 剩余的输入是要转换的标量或下标的向量。

输出向量ind包含转换后的线性索引。

背景:数组元素可以从1开始并贯穿数组中元素数量的线性索引指定,也可以用行、列、页等的下标指定。函数ind2subsub2ind两种形式之间的相互转换。

线性索引遍历维度1(行)、维度2(列)、维度3(页)等,直到对所有元素进行了编号。考虑以下3乘3矩阵:

[(1,1), (1,2), (1,3)]     [1, 4, 7]
[(2,1), (2,2), (2,3)] ==> [2, 5, 8]
[(3,1), (3,2), (3,3)]     [3, 6, 9]

左矩阵包含每个矩阵元素的下标元组。右矩阵显示了同一矩阵的线性索引。

以下示例显示如何转换二维索引(2,1)(2,3)3乘3矩阵到线性索引的一个调用sub2ind.

s1=[2,2];s2=[1,3];ind=sub2ind([3,3],s1,s2)⇒ ind=2 8

详见: ind2sub, 大小.

 
: [s1, s2, …, sN] = ind2sub (dims, ind)

将线性索引转换为下标。

输入dims是一个维度向量,其中每个元素都是相应维度中数组的大小(详见size). 第二个输入ind包含要转换的线性索引。

输出s1, …, sN包含转换后的下标。

背景:数组元素可以从1开始并贯穿数组中元素数量的线性索引指定,也可以用行、列、页等的下标指定。函数ind2subsub2ind两种形式之间的相互转换。

线性索引遍历维度1(行)、维度2(列)、维度3(页)等,直到对所有元素进行了编号。考虑以下3乘3矩阵:

[1, 4, 7]     [(1,1), (1,2), (1,3)]
[2, 5, 8] ==> [(2,1), (2,2), (2,3)]
[3, 6, 9]     [(3,1), (3,2), (3,3)]

左矩阵包含每个矩阵元素的线性索引。右矩阵显示了同一矩阵的下标元组。

以下示例显示如何转换线性索引28到3乘3矩阵的适当下标。

ind=[2,8];[r,c]=ind2sub([3,3],ind)⇒ r=2 2⇒ c=1 3

如果输出下标的数量超过维度的数量,则多余的维度设置为1另一方面,如果提供的下标少于尺寸,则超出的尺寸将合并到最终指定的尺寸中。为了清楚起见,请考虑以下示例:

ind  = [2, 8];
dims = [3, 3];
## same as dims = [3, 3, 1]
[r, c, s] = ind2sub (dims, ind)
    ⇒ r =  2   2
    ⇒ c =  1   3
    ⇒ s =  1   1
## same as dims = [9]
r = ind2sub (dims, ind)
    ⇒ r =  2   8

详见: sub2ind, 大小.

 
: tf= isindex (ind)
: tf= isindex (ind, n)

如果ind是一个有效的索引,返回true。

有效索引可以是正整数(尽管可能是真实数据类型),也可以是逻辑数组。

如果存在,n指定要索引的维度的最大范围。尽可能缓存内部结果,以便使用后继续索引将不再执行检查ind

实现说明:在检查有效索引之前,首先将字符串转换为双值。除非字符串包含NULL字符“\0”,否则它始终是有效的索引。

组件计数不等于维度

具有nd维度的数组可以从1到nd的索引表达式进行索引。对于普通和最常见的情况,则组件的数量M匹配维度数nd。 在这种情况下,应用普通的索引规则,并且每个组件对应于数组的相应维度。

但是,如果索引组件的数量超过尺寸的数量(M>nd)那么多余的组分必须都是单线态(1). 此外,如果M<nd,该行为等效于对输入对象进行整形,以便合并尾部nd-M维度到最后一个索引维度M因此,结果将具有索引表达式的维度,而不是原始对象的维度。只要索引的维度大于1,就会出现这种情况(M>1),使得线性索引的特殊规则不适用。这是最容易理解的一个例子:

A = reshape (1:8, 2, 2, 2)  # Create 3-D array
A =

ans(:,:,1) =

   1   3
   2   4

ans(:,:,2) =

   5   7
   6   8

## 2-D indexing causes third dimension to be merged into second dimension.
## Equivalent array for indexing, Atmp, is now 2x4.
Atmp = reshape (A, 2, 4)
Atmp =

   1   3   5   7
   2   4   6   8


A(2,1)   # Reshape to 2x4 matrix, second entry of first column: ans = 2
A(2,4)   # Reshape to 2x4 matrix, second entry of fourth column: ans = 8
A(:,:)   # Reshape to 2x4 matrix, select all rows & columns, ans = Atmp

请注意,这里优雅地使用了双冒号来代替调用reshape

数组复制

线性索引的另一个高级用途是创建填充有单个值的数组。这可以通过对标量值使用1的索引来实现。结果是一个对象,其索引表达式和每个元素的维度都等于原始标量。例如,以下语句

a = 13;
a(ones (1, 4))

返回其四个元素都等于13的行向量。

类似地,通过用两个一的向量索引标量,可以创建矩阵

a = 13;
a(ones (1, 2), ones (1, 3))

创建一个2x3矩阵,所有元素都等于13。这也可以写成

13(ones (2, 3))

使用索引比代码构造更有效scalar * ones (M, N, …)因为它避免了不必要的倍增操作。此外,可能不会为要复制的对象定义乘法,而总是定义索引数组。以下代码显示如何从本身不是标量的基本单元创建2x3元胞数组。

{"Hello"}(ones (2, 3))

应该注意的是ones (1,n)(一个1的行向量)返回一个对象(增量为零)。范围在内部存储为起始值、增量、结束值和值的总数;因此,当元素的数量大于4时,它比1的向量或矩阵更有效地进行存储。特别是当‘r是行向量,表达式

  r(ones (1, n), :)
  r(ones (n, 1), :)

将返回相同的结果,但第一个结果将明显更快,至少对于rn足够大了。在第一种情况下,索引以压缩形式保存为一个范围,这允许Octave选择更有效的算法来处理表达式。

对于不熟悉这些技术的用户,一般建议使用以下函数repmat用于将较小的数组复制到较大的数组,这使用了这样的技巧。

索引以提高性能

索引的第二个用途是加快代码的速度。索引是一种快速操作,明智地使用它可以减少在单个数组元素上循环的要求,这是一种缓慢的操作。

考虑以下创建10元素行向量的示例a包含值sa(i)=sqrt(i)。

for i = 1:10
  a(i) = sqrt (i);
endfor

使用这样的循环创建向量是非常低效的。在这种情况下,使用表达式会更有效率

a = sqrt (1:10);

这完全避免了循环。

如果无法避免循环,或者必须组合多个值以形成更大的矩阵,则先设置矩阵(预赋值存储)的大小通常更快,然后使用索引命令插入元素。例如,给定一个矩阵a,

[nr, nc] = size (a);
x = zeros (nr, n * nc);
for i = 1:n
  x(:,(i-1)*nc+1:i*nc) = a;
endfor

比如下代码更快

x = a;
for i = 1:n-1
  x = [x, a];
endfor

因为Octave不必重复调整中间结果的大小。

有关更多性能改进建议,详见向量化和更快的代码执行.


版权所有 © 2024 Octave中文网

ICP备案/许可证号:黑ICP备2024030411号