Пример алгоритма 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 ]
Алгоритм очень прост: он проходит через вектор и заменяет все нули последним ненулевым значением. Это кажется тривиальным, и это происходит, когда выполняется с медленным циклом (i = 1: длина) и может ссылаться на предыдущий элемент (i-1), но выглядит невозможен в быстрой векторизованной форме. Я попытался слить() и shift(), но он работает только для первого вхождения нуля, а не из произвольного числа из них.
Можно ли это сделать в векторизованной форме в Octave/Matlab или использовать C для достижения достаточной производительности при большом количестве данных?
Спасибо, Pawel
PS: У меня есть другой подобный медленный алгоритм для цикла для ускорения, и, как правило, невозможно ссылаться на предыдущие значения в векторизованной форме, например, на отставание SQL() или группой по циклу (i-1). Но петли Octave/Matlab ужасно медленны.
Кто-нибудь нашел решение этой общей проблемы или это бесполезно для основных причин дизайна Octave/Matlab?
========== РЕДАКТИРОВАНИЕ ===============
Тест производительности:
==== РЕШЕНИЕ 1 (медленный цикл)
in = out = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] ,1 ,100000);
tic; for i=2:length(out) if (out(i)==0) out(i)=out(i-1); endif; endfor; toc;
[in(1:20); out(1:20)] # test to show side by side if ok
Elapsed time is 15.047 seconds.
==== РЕШЕНИЕ 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
Elapsed time is 0.188167 seconds.
# 15.047 / 0.188167 = 79.97 times improvement
==== РЕШЕНИЕ 3 от GameOfThrows (~ 115 раз быстрее)
in = a = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] ,1 ,100000);
tic;
pada = [a,888];
b = pada(find(pada >0));
bb = b(:,1:end-1);
c = find (pada==0);
d = find(pada>0);
length = d(2:end) - (d(1:end-1));
t = accumarray(cumsum([1,length])',1);
out = R = bb(cumsum(t(1:end-1)));
toc;
Elapsed time is 0.130558 seconds.
# 15.047 / 0.130558 = 115.25 times improvement
==== Магический РЕШЕНИЕ 4 Луиса Мендо (~ 250 раз быстрее)
Обновлен до аккуратного однострочного
in = repmat([ 1 0 2 0 7 7 7 0 5 0 0 0 9 ] , 1, 100000);
tic;
out = nonzeros(in).'(cumsum(in~=0));
toc;
Elapsed time is 0.0597501 seconds.
# 15.047 / 0.0597501 = 251.83 times improvement
Дэн, GameOfThrows и Luis - я очень ценю вашу быструю, острую и эффективную помощь в этом деле. Это отличные решения с отличным ускорением. Я удивлен, что такое улучшение возможно, и сейчас я отправлю второй вызов. Сначала я решил пропустить его, потому что я считал его более трудным и недоступным, но что показывают эти данные - я надеюсь, что я снова не прав.
См. также: Тривиальная/невозможная задача алгоритма в Octave/Matlab Часть II: память итераций