假设我有一个18x2矩阵,其中行被分成块。每个块包含三行,即矩阵由六个块组成。第一列包含块索引,它们从1到6按升序排列。第二列包含实际数据,对于说明性的假设,它是1到18。矩阵看起来像这样:
mat = [ ...
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
3, 7;
3, 8;
3, 9;
4, 10;
4, 11;
4, 12;
5, 13;
5, 14;
5, 15;
6, 16;
6, 17;
6, 18]
我还有一个随机向量,包含6到6范围内的整数元素,例如
perm = [2;4;1;2;4;6]
我现在需要根据烫发的顺序重新排序矩阵。新矩阵应如下所示:
matNew = [ ...
2, 4;
2, 5;
2, 6;
4, 10;
4, 11;
4, 12;
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
4, 10;
4, 11;
4, 12;
6, 16;
6, 17;
6, 18]
我通过使用for循环得到了一个结果,我将各个块顺序复制到matNew。然而,真实矩阵可能大到10,000 x 15并且我需要执行这些排列1000到10000次并将其保存在3D阵列/结构/单元阵列中,无论哪种方式最适合性能。
答案 0 :(得分:1)
注意:如果每个矩阵都有不同的矩阵,这种方法最快一英里
时间,但如果仅perm
更改,则需要大约两倍于erfan代码的时间。
您可以使用perm
为每个bsxfun
元素创建索引列表来执行此操作。 n
是每个索引的元素数。然后使用它来创建所需的列表:
perm_rep = bsxfun(@plus, n*(perm-1), 1:n).'
matNew = mat(perm_rep, :)
matNew =
2 4
2 5
2 6
4 10
4 11
4 12
1 1
1 2
1 3
2 4
2 5
2 6
4 10
4 11
4 12
6 16
6 17
6 18
<强>基准:强>
使用以下数据进行以下基准测试:
sz_mat = size(mat)
num_perms = numel(perm)
sz_mat =
18000 20
num_perms =
1000
这些函数适用于具有不同矩阵大小的函数,并使用timeit
计时。上述方法比erfan的方法快约200倍,比rahnema1快3倍。
f = @() erfan(mat, perm);
g = @() stewie(mat, perm);
h = @() rahnema1(mat, perm);
isequal(f(), g(), h());
fprintf('Erfan: %f s\nStewie: %f s\nrahnema1: %f s\n',timeit(f), timeit(g), timeit(h));
Erfan: 0.003353 s
Stewie: 0.000139 s
rahnema1: 0.000620 s
答案 1 :(得分:1)
由于你想要应用许多排列(虽然它实际上不是排列),但最快的方法是将每个块分开并根据perm
连接它们:
% in RESHAPE: 3: # of rows per block, 2: # of columns
matblock = permute(reshape(mat.', 2, 3, []), [2, 3, 1]);
% perm = [2;4;1;2;4;6];
matNew = reshape(matblock(:,perm,:), [], 2, 1); % 2: same as above
这样做的另一种方式看起来更简单:
id = reshape(1:18, 3,[]);
matNew = mat(reshape(id(:, perm), [], 1),:);
请注意,在两个解决方案中,第一个命令在开头执行一次,而第二个命令在每次更新perm
时运行。
由于OP想要对同一个mat
应用多个排列,我会以这种方式进行基准测试:
与perm
无关的代码的所有部分都执行一次。 perm
所涉及的部分将为所提供的mat
执行100000次:
------------------- With Erfan's first solution:
Elapsed time is 3.979285 seconds.
------------------- With Erfan's second solution:
Elapsed time is 4.245531 seconds.
------------------- With Stewie's solution:
Elapsed time is 6.998606 seconds.
------------------- With rahnema1's solution
Elapsed time is 8.278994 seconds.
基准测试更新:
我尝试了不同的基准测试,这次使用10000 x 15 mat
,以这种方式运行10000次:
%%%%%%%%%%%%%%%% common code which runs once
mat = rand(10000, 15);
mat(:, 1) = reshape(repmat(1:2000, 5, 1), [], 1);
n = 5; % rows each block
perm = randi(2000, 6, 1);
spr = sparse(1:size(mat,1), mat(:,1),1:size(mat,1));
matblock = permute(reshape(mat.', 15, n, []), [2, 3, 1]);
id = reshape(1:10000, n, []);
%%%%%%%%%%%%%%%%
turn = 10000;
disp('------------------- With Erfan''s first solution:')
tic, for ii = 1:turn, Native; end, toc
disp('------------------- With Erfan''s second solution:')
tic, for ii = 1:turn, Native2; end, toc
disp('------------------- With Stewie''s solution:')
tic, for ii = 1:turn, Alter; end, toc
disp('------------------- With rahnema1''s solution:')
tic, for ii = 1:turn, Alter2; end, toc
这些是脚本:
%%% Native
matNew1 = reshape(matblock(:,perm,:), [], 15, 1);
%%% Native2
matNew2 = mat(reshape(id(:, perm), [], 1),:);
%%% Alter
perm_rep = bsxfun(@plus, n*(perm-1), 1:n).';
matNew3 = mat(perm_rep, :);
%%% Alter2
matNew4 = mat(nonzeros(spr(:,perm)),:);
以下是我的基准测试结果。实际上这里没有太大差异,除非需要不断进行这种计算。否则,只需要使用哪个代码。
------------------- With Erfan's first solution:
Elapsed time is 0.502261 seconds.
------------------- With Erfan's second solution:
Elapsed time is 0.495179 seconds.
------------------- With Stewie's solution:
Elapsed time is 0.805442 seconds.
------------------- With rahnema1's solution:
Elapsed time is 0.529585 seconds.
答案 2 :(得分:1)
您可以使用稀疏矩阵来存储行的索引。然后使用烫发来提取所需元素的指数。此方法适用于不同的块大小:
mat = [ ...
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
3, 7;
3, 8;
3, 9;
4, 10;
4, 11;
4, 12;
5, 13;
5, 14;
5, 15;
6, 16;
6, 17;
6, 18];
perm = [2;4;1;2;4;6];
spr = sparse(1:size(mat,1), mat(:,1),1:size(mat,1));
out = mat(nonzeros(spr(:,perm)),:)