2015-10-30 3 views
2

Я следующий цикл в MATLAB:Vectorize этот цикл

n = 20000 
rho=0.9; 
sigma=[0.1 0.2 0.3]; 
epsilon = normrnd(0,1, 3, n); 
z = NaN(1,n); 
z(1,1) = 0; 
for i=2:n 
    z(1,i) = rho * z(1,i-1) + sigma* epsilon(:,i); 
end 

Я пытался векторизации его, выполнив:

z(1,2:end) = rho * z(1,1:end-1) + sigma * epsilon 

Это не сработало. Я понимаю, что проблема в том, что этот бит: z(1,2:end) = rho * z(1,1:end-1) не является рекурсивным.

Как я могу это решить?

+1

Проблема в том, что каждый элемент зависит от предыдущего элемента, отсюда и требование цикла. Возможно, 'bsxfun' может его решить, но для рекурсивных функций я всегда использую цикл. – Adriaan

+0

Это очень сложно для векторизации, кроме того, цикл for очень быстрый. В моей системе пример занимает менее 0,02. Если ваша фактическая проблема намного больше, я не думаю, что это того стоит. – Daniel

+0

Зачем вы хотите его прорисовать? – IKavanagh

ответ

6

В пост апокалиптическом мире, заполненном crazy fast parallel processors and almost-free memory latency machines, где векторизации инструменты, такие как bsxfun и matrix multiplication легко может породить через 20000 ядер, один потерянная душа отчаянно пытается векторизации такой проблемы может рисковать в чем-то вроде этого -

parte1 = tril(rho.^(bsxfun(@minus,(0:n-2)',0:n-2))); 
parte2 = sigma*epsilon(:,2:end); 
z = [0 parte2*parte1.'] 

Со всей серьезностью, это не должно быть эффективно использует память и, таким образом, скорее всего, не будет быстро .. Теперь, но призван продемонстрировать способ проследить рекурсию и применять его для пр екторизованное решение.

+0

Вы можете использовать 'parte1 = tril (rho.^[0: n-2] '* rho.^(0: -1: -n + 2));', чтобы избежать n^2 вычислений мощности, что является самой медленной частью кода. – Daniel

+0

Или 'parte1 = toeplitz (rho.^[0: n-2], [1, нули (1, n-2)]);' – Daniel

+1

@ Daniel Спасибо! Они могут быть использованы, а также возникает вопрос о создании такой матрицы, как 'parte1' в [' this one'] (http://stackoverflow.com/questions/29209361/replicate-vector-and-shift-each-copy- по-1-рядного вниз-без для цикла). Это сообщение предназначалось скорее как руководство для отслеживания рекурсий, подобных этим. Для производительности частично векторизованный подход в вашем решении стал бы решением. – Divakar

4

Частичная прорисовка кода снизила время выполнения для вашей формы от 0,015 до 0,0005s в моей системе. Я просто заранее рассчитанным sigma* epsilon(:,i) с использованием одного матричного умножения:

n = 20000 
rho=0.9; 
sigma=[0.1 0.2 0.3]; 
epsilon = normrnd(0,1, 3, n); 
se=sigma*epsilon; 
z = NaN(1,n); 
z(1,1) = 0; 
for i=2:n 
    z(1,i) = rho * z(1,i-1) + se(i); 
end 
+0

Я думаю, вы имеете в виду 'se (:, i)'? –

+0

'se' имеет размер' [1, n] ', оба являются правильными. – Daniel

+0

О, хорошо. Сигма '1x3', я пропустил это. –

1

Если вы повторно не написать эту конкретную программу таким образом, чтобы не использовать рекурсию, вы не можете векторизации его.

Очень сложно иметь векторизованные вычисления с рекурсией, потому что большую часть времени у вас не может быть выражения закрытой формы для функции, которую вы пытаетесь решить (эффективно в любой момент времени вы не имеете конечного числа операций; и даже если у вас есть конечное число, которое будет экспоненциально расти).

Возможно, вы захотите рассмотреть возможность повторной записи своей функции с помощью filter. Он специально разработан для таких отношений.

EDIT:

Как уже упоминалось, что вы описываете элементарный фильтр. Предполагая, что один и тот же параметр, как @Daniel вы просто:

... 
epsilon = normrnd(0,1, 3, n); 
se=sigma*epsilon; 

a = [1 -rho]; 
b = 1; 
z = filter(b, a, [0 se(2:n)]); 

Я считаю, что вы будете последовательно найти, что это будет быстрее из трех предложенных решений. Для меня это также кажется наиболее естественным, так как ваш оператор проблемы описывает алгоритм фильтрации.