从 3D 数组中的多个页面访问不同的行
Access different rows from multiple pages in 3D array
如何在避免 for
循环的同时访问 3D 数组中多个页面的不同行?
假设我有一个 10x5x3
矩阵 (mat1
),我想从三页中复制不同的行(例如第一页的第 4、2 和 5 行) 、第 2 和第 3 页)放入另一个 10x5x3
矩阵 (mat2
) 的第一行。
我的解决方案使用 for
循环。矢量化呢?
mat1 = randi(100, 10, 5, 3)
mat2 = nan(size(mat1))
rows_to_copy = [4, 2, 5]
for i = 1 : 3
mat2(1, :, i) = mat1(rows_to_copy(i), :, i)
end
任何矢量化解决方案都可能不会像您的 for 循环解决方案那么简单,并且可能实际上效率较低(编辑: 请参阅下面的时序测试)。但是,如果您很好奇,像这样向量化索引操作通常涉及将所需的索引从下标转换为 linear indices. Normally you can do this using sub2ind
,但由于您选择的是整行,因此您自己计算索引可能更有效。
这是一个在较新版本的 MATLAB(R2016b 及更高版本)中利用 implicit expansion 的解决方案:
[R, C, D] = size(mat1);
index = rows_to_copy+R.*(0:(C-1)).'+R*C.*(0:(D-1));
mat2(1, :, :) = reshape(mat1(index), 1, C, D);
请注意,如果您真的不需要 mat2
中充满 NaN
值的所有额外 space,您可以通过连接所有行来使结果更紧凑改为二维矩阵:
>> mat2 = mat1(index).'
mat2 =
95 41 2 19 44
38 31 93 27 27
49 10 72 91 49
如果您仍在使用没有隐式扩展的旧版本 MATLAB,则可以改用 bsxfun
:
index = bsxfun(@plus, rows_to_copy+R*C.*(0:(D-1)), R.*(0:(C-1)).');
时机
我 运行 使用 timeit
(R2018a,Windows 7 64 位)进行了一些测试,以查看循环和索引解决方案的比较情况。我测试了 3 种不同的场景:增加行大小、增加列大小和增加 mat1
的页面大小(第三维)。 rows_to_copy
是 运行domly 选择的,并且始终具有与 mat1
的页面大小相同的元素数。以下是结果,显示了循环时间与索引时间的比率:
除了一些 t运行sient 噪声,还有一些清晰的模式。增加行数或列数(蓝线或红线)不会明显改变时间比率,时间比率徘徊在 运行ge 的 0.7 到 0.9 之间,这意味着 for 循环平均略快一些。增加页面数量(黄线)意味着 for 循环必须迭代更多次,索引解决方案很快开始胜出,当页面大小超过 150 时达到 8 倍加速。
如何在避免 for
循环的同时访问 3D 数组中多个页面的不同行?
假设我有一个 10x5x3
矩阵 (mat1
),我想从三页中复制不同的行(例如第一页的第 4、2 和 5 行) 、第 2 和第 3 页)放入另一个 10x5x3
矩阵 (mat2
) 的第一行。
我的解决方案使用 for
循环。矢量化呢?
mat1 = randi(100, 10, 5, 3)
mat2 = nan(size(mat1))
rows_to_copy = [4, 2, 5]
for i = 1 : 3
mat2(1, :, i) = mat1(rows_to_copy(i), :, i)
end
任何矢量化解决方案都可能不会像您的 for 循环解决方案那么简单,并且可能实际上效率较低(编辑: 请参阅下面的时序测试)。但是,如果您很好奇,像这样向量化索引操作通常涉及将所需的索引从下标转换为 linear indices. Normally you can do this using sub2ind
,但由于您选择的是整行,因此您自己计算索引可能更有效。
这是一个在较新版本的 MATLAB(R2016b 及更高版本)中利用 implicit expansion 的解决方案:
[R, C, D] = size(mat1);
index = rows_to_copy+R.*(0:(C-1)).'+R*C.*(0:(D-1));
mat2(1, :, :) = reshape(mat1(index), 1, C, D);
请注意,如果您真的不需要 mat2
中充满 NaN
值的所有额外 space,您可以通过连接所有行来使结果更紧凑改为二维矩阵:
>> mat2 = mat1(index).'
mat2 =
95 41 2 19 44
38 31 93 27 27
49 10 72 91 49
如果您仍在使用没有隐式扩展的旧版本 MATLAB,则可以改用 bsxfun
:
index = bsxfun(@plus, rows_to_copy+R*C.*(0:(D-1)), R.*(0:(C-1)).');
时机
我 运行 使用 timeit
(R2018a,Windows 7 64 位)进行了一些测试,以查看循环和索引解决方案的比较情况。我测试了 3 种不同的场景:增加行大小、增加列大小和增加 mat1
的页面大小(第三维)。 rows_to_copy
是 运行domly 选择的,并且始终具有与 mat1
的页面大小相同的元素数。以下是结果,显示了循环时间与索引时间的比率:
除了一些 t运行sient 噪声,还有一些清晰的模式。增加行数或列数(蓝线或红线)不会明显改变时间比率,时间比率徘徊在 运行ge 的 0.7 到 0.9 之间,这意味着 for 循环平均略快一些。增加页面数量(黄线)意味着 for 循环必须迭代更多次,索引解决方案很快开始胜出,当页面大小超过 150 时达到 8 倍加速。