假设c
是标量值,T
和W
是M-by-N
矩阵,k
是另一个M-by-N
矩阵,其中包含来自的值1
至M
(并且至少有两对(i1, j1)
,(i2, j2)
,k(i1, j1)==k(i2, j2)
}和a
为1-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
您是否有任何提示,以便我可以对此代码进行矢量化(或者通常更快)?
提前致谢!
答案 0 :(得分:2)
这是一种结合bsxfun
和accumarray
的方法 -
% 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/a
:2D
到W
:>> 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
但是,如果a
和c/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's,Divakar'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