MATLAB - 重新排序行块

时间:2016-10-06 08:40:52

标签: matlab

假设我有一个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阵列/结构/单元阵列中,无论哪种方式最适合性能。

3 个答案:

答案 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)),:)