【发布时间】:2016-03-06 15:03:59
【问题描述】:
Matlab/Octave 算法示例:
input vector: [ 1 0 2 0 7 7 7 0 5 0 0 0 9 ]
output vector: [ 1 1 2 2 7 7 7 7 5 5 5 5 9 ]
该算法非常简单:它遍历向量并将所有零替换为最后一个非零值。这似乎微不足道,当使用慢速 for (i=1:length) 循环并能够引用前一个元素 (i-1) 时也是如此,但看起来不可能以快速矢量化形式表达。 我尝试了 merge() 和 shift() 但它只适用于第一次出现的零,而不是任意数量的零。
可以在 Octave/Matlab 中以矢量化形式完成,还是必须使用 C 才能在大量数据上具有足够的性能?
我有another similar slow for-loop algorithm to speed up,并且似乎通常不可能以矢量化形式引用以前的值,例如 SQL lag() 或 group by 或 loop (i-1) 很容易做到。但是 Octave/Matlab 循环非常慢。
有没有人找到解决这个普遍问题的方法,或者这对于基本的 Octave/Matlab 设计原因是徒劳的?
性能基准:
解决方案 1(慢循环)
in = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] ,1 ,100000);
out = in;
tic
for i=2:length(out)
if (out(i)==0)
out(i)=out(i-1);
end
end
toc
[in(1:20); out(1:20)] % test to show side by side if ok
经过的时间是 15.047 秒。
Dan 的解决方案 2(快约 80 倍)
in = V = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] ,1 ,100000);
tic;
d = double(diff([0,V])>0);
d(find(d(2:end))+1) = find(diff([0,~V])==-1) - find(diff([0,~V])==1);
out = V(cumsum(~~V+d)-1);
toc;
[in(1:20); out(1:20)] % shows it works ok
经过的时间是 0.188167 秒。
15.047 / 0.188167 = 79.97 倍改进
GameOfThrows 的解决方案 3(快约 115 倍)
in = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] ,1 ,100000);
a = in;
tic;
pada = [a,888];
b = pada(pada >0);
bb = b(:,1:end-1);
c = find (pada==0);
d = find(pada>0);
len = d(2:end) - (d(1:end-1));
t = accumarray(cumsum([1,len])',1);
out = bb(cumsum(t(1:end-1)));
toc;
经过的时间是 0.130558 秒。
15.047 / 0.130558 = 115.25 倍改进
Magical Luis Mendo 解决方案 4(快约 250 倍)
in = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] , 1, 100000);
tic;
u = nonzeros(in);
out = u(cumsum(in~=0)).';
toc;
经过的时间是 0.0597501 秒。
15.047 / 0.0597501 = 251.83 倍改进
(2019/03/13 更新)使用 MATLAB R2017a 的时序:
Slow loop: 0.010862 seconds.
Dan: 0.072561 seconds.
GameOfThrows: 0.066282 seconds.
Luis Mendo: 0.032257 seconds.
fillmissing: 0.053366 seconds.
所以我们再次得出同样的结论:MATLAB 中的循环不再慢!
另请参阅: Trivial/impossible algorithm challenge in Octave/Matlab Part II: iterations memory
【问题讨论】:
-
@GameOfThrows 是的,我想是的。刚刚添加了我的尝试
-
@Pawel 您能否在您的 Octave 中为我们做一个
timeit比较两个答案和一个幼稚的for-loop 解决方案?看看这些选项是否能提高性能会很有趣 -
@Dan 肯定在努力
-
@Pawel 可能比
isequal更易于使用[in(1:20); out(1:20)] # test to show side by side if ok。 -
@Pawel 你能不能也试试 Luis Mendo 的解决方案,我猜它应该是最快的,也是最干净的。
标签: arrays matlab loops octave vectorization