Как эффективно нормализовать вектор в MATLAB? Любая связанная встроенная функция?

42

Я нормализую вектор V в MATLAB следующим образом:

normalized_V = V/norm(V);

однако, является ли он самым элегантным (эффективным) способом нормализации вектора в MATLAB?

  • 0
    Разве это не норма L1, которую нужно принимать при нормализации? нормализованный = v / норма (v, 1);
Теги:
performance
vector

6 ответов

40
Лучший ответ

Исходный код, который вы предлагаете, является лучшим способом.

Matlab чрезвычайно хорош в векторизованных операциях, таких как это, по крайней мере для больших векторов.

Встроенная нормальная функция выполняется очень быстро. Вот некоторые результаты синхронизации:

V = rand(10000000,1);
% Run once
tic; V1=V/norm(V); toc           % result:  0.228273s
tic; V2=V/sqrt(sum(V.*V)); toc   % result:  0.325161s
tic; V1=V/norm(V); toc           % result:  0.218892s

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

Информация о синхронизации здесь была выпущена с R2008a x64 в Windows.


EDIT:

Пересмотренный ответ на основе предложений gnovice (см. комментарии). Матричная математика (едва) выигрывает:

clc; clear all;
V = rand(1024*1024*32,1);
N = 10;
tic; for i=1:N, V1 = V/norm(V);         end; toc % 6.3 s
tic; for i=1:N, V2 = V/sqrt(sum(V.*V)); end; toc % 9.3 s
tic; for i=1:N, V3 = V/sqrt(V'*V);      end; toc % 6.2 s ***
tic; for i=1:N, V4 = V/sqrt(sum(V.^2)); end; toc % 9.2 s
tic; for i=1:N, V1=V/norm(V);           end; toc % 6.4 s

IMHO, разница между "norm (V)" и "sqrt (V '* V)" достаточно мала, что для большинства программ лучше всего пойти с тем, что более понятно. Для меня "норма (V)" более понятна и легче читать, но "sqrt (V '* V)" все еще идиоматична в Matlab.

  • 1
    Просто из любопытства, как быстро они будут бегать ?: V3 = V / sqrt (V '* V); V4 = V / sqrt (сумма (V. ^ 2));
  • 2
    Я согласен, что норма (V) является наиболее прямым ответом, так как при использовании sqrt (V '* V) выигрыш в скорости невелик. Ускорение было бы еще менее значительным для типичного трехэлементного вектора, чем V обычно я использую норму.
Показать ещё 4 комментария
15

Я не знаю ни одного MATLAB, и я никогда не использовал его, но мне кажется, что вы делите. Зачем? Что-то вроде этого будет намного быстрее:

d = 1/norm(V)
V1 = V * d
  • 0
    И это дает еще 30% ускорения в Октаве. Хорошо подмечено
  • 0
    Почему это быстрее? Вы все еще делите 1.
Показать ещё 2 комментария
9

Единственная проблема, с которой вы столкнулись, - это норма нуля V равна нулю (или очень близко к ней). Это может дать вам Inf или NaN, когда вы разделите вместе с предупреждением о делении на ноль. Если вы не хотите получать Inf или NaN, вы можете включить или выключить предупреждение, используя WARNING:

oldState = warning('off','MATLAB:divideByZero');  % Return previous state then
                                                  %   turn off DBZ warning
uV = V/norm(V);
warning(oldState);  % Restore previous state

Если вам не нужны значения Inf или NaN, сначала необходимо проверить размер нормы:

normV = norm(V);
if normV > 0,  % Or some other threshold, like EPS
  uV = V/normV;
else,
  uV = V;  % Do nothing since it basically 0
end

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

3

Я взял код г-на Фуза, а также добавил решение Арлена, и вот тайминги, которые я получил для Octave:

clc; clear all;
V = rand(1024*1024*32,1);
N = 10;
tic; for i=1:N, V1 = V/norm(V);         end; toc % 7.0 s
tic; for i=1:N, V2 = V/sqrt(sum(V.*V)); end; toc % 6.4 s
tic; for i=1:N, V3 = V/sqrt(V'*V);      end; toc % 5.5 s
tic; for i=1:N, V4 = V/sqrt(sum(V.^2)); end; toc % 6.6 s
tic; for i=1:N, V1 = V/norm(V);         end; toc % 7.1 s
tic; for i=1:N, d = 1/norm(V); V1 = V*d;end; toc % 4.7 s

Затем, из-за того, что я сейчас просматриваю, я тестировал этот код для обеспечения того, чтобы каждая строка суммировалась до 1:

clc; clear all;
m = 2048;
V = rand(m);
N = 100;
tic; for i=1:N, V1 = V ./ (sum(V,2)*ones(1,m));                end; toc % 8.2 s
tic; for i=1:N, V2 = bsxfun(@rdivide, V, sum(V,2));            end; toc % 5.8 s
tic; for i=1:N, V3 = bsxfun(@rdivide, V, V*ones(m,1));         end; toc % 5.7 s
tic; for i=1:N, V4 = V ./ (V*ones(m,m));                       end; toc % 77.5 s
tic; for i=1:N, d = 1./sum(V,2);V5 = bsxfun(@times, V, d);     end; toc % 2.83 s
tic; for i=1:N, d = 1./(V*ones(m,1));V6 = bsxfun(@times, V, d);end; toc % 2.75 s
tic; for i=1:N, V1 = V ./ (sum(V,2)*ones(1,m));                end; toc % 8.2 s
2

Рационально сделать все умножение, я добавляю запись в конце списка

    clc; clear all;
    V = rand(1024*1024*32,1);
    N = 10;
    tic; for i=1:N, V1 = V/norm(V);         end; toc % 4.5 s
    tic; for i=1:N, V2 = V/sqrt(sum(V.*V)); end; toc % 7.5 s
    tic; for i=1:N, V3 = V/sqrt(V'*V);      end; toc % 4.9 s
    tic; for i=1:N, V4 = V/sqrt(sum(V.^2)); end; toc % 6.8 s
    tic; for i=1:N, V1 = V/norm(V);         end; toc % 4.7 s
    tic; for i=1:N, d = 1/norm(V); V1 = V*d;end; toc % 4.9 s
    tic; for i=1:N, d = norm(V)^-1; V1 = V*d;end;toc % 4.4 s
0

Самый быстрый (время по сравнению с Джейкобсом):

clc; clear all;
V = rand(1024*1024*32,1);
N = 10;
tic; 
for i=1:N, 
    d = 1/sqrt(V(1)*V(1)+V(2)*V(2)+V(3)*V(3)); 
    V1 = V*d;
end; 
toc % 1.5s

Ещё вопросы

Сообщество Overcoder
Наверх
Меню