加速迭代 - MATLAB

时间:2014-06-26 07:28:08

标签: matlab vectorization performance

考虑2个向量A = [20000000 x 1]B = [20000000 x 1 ]

我需要找到对应于B的每个唯一元素的所有A的总和。

虽然这看起来很简单,但这在MATLAB中将永远存在。

目前,我正在使用

u = unique(B);
length_u = length(u);
C = zeros(length_u,1);

for i = 1:length_u
   C(i,1) = sum(A(B==u(i)));
end

有没有让它跑得更快?我尝试使用并行计算工具箱拆分循环并运行2 parfor个循环(因为我只有2个核心)。还需要几个小时。

P.S:是的,我应该买一台更好的电脑。

4 个答案:

答案 0 :(得分:6)

您必须先看this answer 如果必须,您可以使用histcaccumarray

的组合
A = randi( 500, 1, 100000 );
B = randi( 500, 1, 100000 );

ub = unique( B );

[ignore idx] = histc( B, [ub-.5 ub(end)+.5] );
C = accumarray( idx', A' )';

ideone上查看与幼稚for - 循环实施的玩具比较。

它是如何运作的?

我们使用histc的第二个结果将B(以及后来的A)的元素映射到ub元素定义的区间( B)。
然后使用accumarrayA所有条目的所有条目与idx定义的映射相加。
注意:我假设B的独特元素至少相差0.5。

答案 1 :(得分:3)

如果B仅包含整数,则可以使用sparse添加具有相同索引的元素的事实在一行中轻松完成:

C = nonzeros(sparse(B,1,A));

答案 2 :(得分:3)

进一步简化 Shai

建议的代码
A = randi( 500, 1, 100000 );
B = randi( 500, 1, 100000 );

[~,~,idb] = unique( B );

C = accumarray( idb', A' )';

此处"idb"Shai建议的代码中提供与"idx"相同的向量。

答案 3 :(得分:1)

我修改了总和。而不是必须检查每个元素而不是它适合大小写(B==u(i)),我对数组进行了排序,并在元素发生变化时停止了。从该元素开始下一个总和。这样我只需要在A中循环每个元素,而不是length_u次。这是我使用的代码:

A= rand(100000,1);
B= round(rand(100000,1)*25000);
u = unique(B);
length_u = length(u);
C = zeros(length_u,1);
E = zeros(length_u,1);
tic;
for k = 1:length_u
   C(k,1) = sum(A(B==u(k)));
end
t_OP=toc;

tic
D= sortrows([A,B],2);
n=1;
for l=1:numel(u)
    m=n;
    while m<numel(B) && D(m+1,2)==u(l) 
        m=m+1;
    end
    E(l,1) = sum(D(n:m,1));
    n=m+1;
end
t_trial=toc;
display(t_OP)
display(t_trial)

我也使用了你的代码。您的代码所用的时间为:t_OP=10.9398,我的修改时间为t_trial=0.0962。希望这会有所帮助。我通过构建sum(E-C) 0来确保代码有效。
编辑:Speedtest
我将它与 @Shai 的解决方案进行了比较。这导致了

t_OP =

   10.8147
t_trial =

    0.0984
t_Shai =

    0.0154


编辑:@moarningsun评论
 而不是使用while - 循环。如果在构建总和之前对数组进行排序,则可以使用unique的第二个输出。

tic
A = randi( 25000, 1, 100000 );
B = randi( 25000, 1, 100000 );
D= sortrows([A',B'],2);
[u, idx] = unique(D(:,2));
idx = [idx; numel(D(:,2))+1];
for l=1:numel(u)
    E(l,1) = sum(D(idx(l):idx(l+1)-1,1));
end
t_trial=toc;