赋值中的索引之间的映射不是单射的

时间:2016-12-27 13:22:15

标签: matlab performance vectorization

假设c是标量值,TWM-by-N矩阵,k是另一个M-by-N矩阵,其中包含来自的值1M(并且至少有两对(i1, j1)(i2, j2)k(i1, j1)==k(i2, j2)}和a1-by-M }向量。我想对以下代码进行矢量化(希望这会加快它的速度):

T = zeros(M,N);
for j = 1:N
    for i = 1:M
        T(k(i,j),j) = T(k(i,j),j) + c*W(i,j)/a(i);
    end
end

您是否有任何提示,以便我可以对此代码进行矢量化(或者通常更快)?

提前致谢!

3 个答案:

答案 0 :(得分:2)

这是一种结合bsxfunaccumarray的方法 -

% Create 2D array of unique IDs along each col to be used as flattened subs
id = bsxfun(@plus,k,M*(0:N-1));

% Compute "c*W(i,j)/a(i)" for all i's and j's
cWa = c*bsxfun(@rdivide,W,a);

% Accumulate final result for all cols
out = reshape(accumarray(id(:),reshape(cWa,[],1),[M*N 1]),[M,N]);

基准

作为功能的方法 -

function out = func1(W,a,c,k,M,N)
id = bsxfun(@plus,k,M*(0:N-1));
cWa = c*bsxfun(@rdivide,W,a);
out = reshape(accumarray(id(:),reshape(cWa,[],1),[M*N 1]),[M,N]);

function T = func2(W,a,c,k,M,N)  % @rahnema1's solution
[I J] = meshgrid(1:M,1:N);
idx1 = sub2ind([M N], I ,J);
R = c.* W(idx1) ./ a(I);
T = accumarray([k(idx1(:)) ,J(:)], R(:),[M N]);

function T = func3(W,a,c,k,M,N)  % Original approach
T = zeros(M,N);
for j = 1:N
    for i = 1:M
        T(k(i,j),j) = T(k(i,j),j) + c*W(i,j)/a(i);
    end
end

function T = func4(W,a,c,k,M,N) % @gnovice's solution
T = zeros(M, N);
for col = 1:N
  T(:, col) = accumarray(k(:,col), c*W(:, col)./a, [M 1]);
end

机器设置:Kubuntu 16.04,MATLAB 2012a,4GB RAM。

时间码 -

% Setup inputs
M = 3000;
N = 3000;
W = rand(M,N);
a = rand(M,1);
c = 2.34;
k = randi([1,M],[M,N]);

disp('------------------ With func1')
tic,out = func1(W,a,c,k,M,N);toc
clear out

disp('------------------ With func2')
tic,out = func2(W,a,c,k,M,N);toc
clear out

disp('------------------ With func3')
tic,out = func3(W,a,c,k,M,N);toc
clear out

disp('------------------ With func4')
tic,out = func4(W,a,c,k,M,N);toc

运行时间码 -

------------------ With func1
Elapsed time is 0.215591 seconds.
------------------ With func2
Elapsed time is 1.555373 seconds.
------------------ With func3
Elapsed time is 0.572668 seconds.
------------------ With func4
Elapsed time is 0.291552 seconds.

建议方法的可能改进

1]在c*bsxfun(@rdivide,W,a)中,我们使用两个broadcasting阶段 - 一个bsxfun(@rdivide,W,a),其中a被广播;第二个是c广播匹配bsxfun(@rdivide,W,a)的2D输出,但我们不需要bsxfun这个。c。因此,如果我们将 insert-in a除以c,其中1D将仅广播到2D,则可能会有所改进而不是1D,然后第二级广播将是c/a2DW>> tic, c*bsxfun(@rdivide,W,a); toc Elapsed time is 0.073244 seconds. >> tic, bsxfun(@times,W,c/a); toc Elapsed time is 0.041745 seconds. 就像之前一样。这种微小的改进可以定时 -

c

但是,如果ac/a差异很大,缩放系数internal会明显影响最终结果。所以,需要小心这个建议。

答案 1 :(得分:2)

由于k只影响列中的值,而不是列之间的聚合值,因此可以通过将问题减少到在列上单个循环并使用accumarray,如下所示:

T = zeros(M, N);
for col = 1:N
  T(:, col) = accumarray(k(:,col), c*W(:, col)./a, [M 1]);
end

我测试了每个解决方案(问题中的循环,rahnema'sDivakar's和我的),使用在Divakar的答案中初始化的输入值取100次迭代的平均值。这是我得到的(运行Windows 7 x64,16 GB RAM,MATLAB R2016b):

solution | avg. time (s) | max(abs(err)) 
---------+---------------+---------------
loop     |       0.12461 | 0 
rahnema  |       0.84518 | 0 
divakar  |       0.12381 | 1.819e-12 
gnovice  |       0.09477 | 0 

外卖:循环实际上并不是那么糟糕,但如果你可以将它们简化为一个,它可以节省你一点时间。

答案 2 :(得分:1)

可能的解决方案:

data

没有jit的Octave中不同方法的比较:

newtype