2014-02-11 7 views
2

Я пишу приложение Matlab, которое вычисляет какую-то матрицу. Я пытаюсь заменить циклы for в моей программе на основе векторных вычислений, однако, я застрял.Как векторизовать цикл 'for' в Matlab

До сих пор я понял, что это просто образец часть кода:

kernel = zeros(5,5,5); 
offset = 3; 
sig=1; 

for x=-2:2 
    for y=-2:2 
     for z=-2:2 
      IN = x.^2/(sig^2) + y.^2/(sig^2) + z.^2/(sig^2); 
      kernel(offset+x, offset+y, offset+z) = exp(-IN/2); 
     end 
    end 
end 

можно заменить такой конструкции:

[x,y,z] = ndgrid(-2:2,-2:2,-2:2); 
IN = x.^2/(sig^2) + y.^2/(sig^2) + z.^2/(sig^2); 
kernel = exp(-IN/2); 

и дают одинаковые результаты. Но что, если мне нужно сделать некоторые небольшие изменения:

kernel = zeros(5,5,5); 
offset = 3; 
sig=1; 

%sample 3x3 matrix 
R=magic(3)/10; 

for x=-2:2 
    for y=-2:2 
     for z=-2:2 

      % calculation of new x, y, z 
      point = [x,y,z]*R; 

      IN = (point(1)^2)/(sig^2) + (point(2)^2)/(sig^2) + (point(3)^2)/(sig^2); 
      kernel(offset+x, offset+y, offset+z) = exp(-IN/2); 
     end 
    end 
end 

Как я могу ускорить эту конструкцию? Можно ли легко векторизовать? Я совершенно новичок в Matlab, поэтому я был бы признателен за любую помощь. Большое спасибо!

ответ

3

Один из вариантов - использовать arrayfun.

sig=1; 

%sample 3x3 matrix 
R=magic(3)/10; 

[x,y,z] = ndgrid(-2:2,-2:2,-2:2); 
kernel = arrayfun(@(x, y, z) exp(-(norm([x,y,z]*R/sig)^2)/2), x,y,z); 

Пояснение:

arrayfun принимает функцию, которая действует на скалярной вход (ы) для получения скалярного выхода (ов), а также массивы входов, чтобы перейти к функции. Затем он перебирает входные массивы, запускает вашу функцию на каждом из них и помещает вывод каждого в соответствующую запись в выходной матрице (-es). Итак, arrayfun в основном делает вложенный цикл, который замедляет все для вас.

В этом примере я также использовал anonymous function (также называемую лямбда-функцией) для выполнения работы во внутреннем цикле. Поскольку лямбда-функции должны быть одним выражением в Matlab, мне пришлось немного переписать внутренний цикл (используя простые алгебраические манипуляции). Если вам нужно использовать arrayfun с функцией, которая не легко выражается в виде лямбда, вы всегда можете записать эту функцию в отдельный файл .m и передать ее в arrayfun.

EDIT: обратите внимание, что вам больше не нужно назначать kernel, и вам также не нужно offset.

+0

Отличный ответ, спасибо! Работает как шарм. Странно, однако, цикл работает в несколько раз быстрее в этом случае ... Очевидно, arrayfun слишком оптимизирован. – user3299285

+0

@ user3299285 Для меня версия arrayfun была намного быстрее. Многое зависит от того, сколько у вас циклов и сколько работы вы выполняете на каждой итерации. Для такой маленькой петли (всего 27 итераций) ее очень быстро начать с абсолютных значений, так что ее не так много. Кроме того, норма берет квадратный корень (который медленнее по сравнению с другими арифметическими операциями) только для того, чтобы возвращаться назад и сразу округлять результаты. Если вы хотите лучше сравнить скорость цикла vs arrayfun, замените тело цикла той же функцией, что и arrayfun. –

+1

Боюсь, я не могу согласиться. Даже с заменой тела на ту же функцию я получил лучшие результаты, чем с arrayfun. Кажется, что arrayfun действительно не слишком быстро: http://stackoverflow.com/questions/12522888/arrayfun-can-be-significantly-slower-than-an-explicit-loop-in-matlab-why – user3299285

Смежные вопросы