wake-up-neo.com

Wie kann man einen Vektor in MATLAB effizient normalisieren? Eine verwandte eingebaute Funktion?

Ich normalisiere einen Vektor V in MATLAB wie folgt:

normalized_V = V/norm(V);

ist es jedoch der eleganteste (effizienteste) Weg, einen Vektor in MATLAB zu normalisieren?

45
Kamran Bigdely

Der ursprüngliche Code, den Sie vorschlagen, ist der beste Weg.

Matlab eignet sich hervorragend für solche vektorisierten Operationen, zumindest für große Vektoren.

Die eingebaute Normfunktion ist sehr schnell. Hier sind einige Timing-Ergebnisse:

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 wird hier ein zweites Mal berechnet, um sicherzustellen, dass beim ersten Aufruf keine wichtigen Cache-Strafen auftreten.

Die Timing-Informationen hier wurden mit R2008a x64 unter Windows erstellt.


BEARBEITEN:

Überarbeitete Antwort basierend auf den Vorschlägen von gnovice (siehe Kommentare). Matrix-Mathematik (kaum) gewinnt:

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 ist der Unterschied zwischen "norm (V)" und "sqrt (V '* V)" klein genug, dass es für die meisten Programme am besten ist, mit dem klareren zu gehen. Für mich ist "norm (V)" klarer und lesbarer, aber "sqrt (V '* V)" ist in Matlab immer noch idiomatisch.

39
Mr Fooz

Ich kenne kein MATLAB und habe es nie benutzt, aber es scheint mir, dass Sie sich teilen. Warum? So etwas wird viel schneller sein:

d = 1/norm(V)
V1 = V * d
16
Arlen

Das einzige Problem, auf das Sie stoßen würden, ist, wenn die Norm von V Null ist (oder sehr nahe daran ist). Dadurch können Sie Inf oder NaN erhalten, wenn Sie teilen, und eine Warnung durch Null teilen. Wenn Sie kein Inf oder NaN erhalten möchten, können Sie die Warnung einfach mit WARNING ein- und ausschalten:

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

Wenn Sie keine Inf - oder NaN - Werte wünschen, müssen Sie zuerst die Größe der Norm überprüfen:

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

Wenn ich es in einem Programm brauche, gebe ich den obigen Code normalerweise in meine eigene Funktion ein, die normalerweise unit genannt wird (da im Wesentlichen ein Vektor in einen Einheitsvektor umgewandelt wird, der in dieselbe Richtung zeigt).

9
gnovice

Ich habe Herrn Fooz 'Code genommen und auch die Lösung von Arlen hinzugefügt. Hier sind die Timings, die ich für Octave bekommen habe:

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

Dann habe ich aus Gründen, die ich gerade anschaue, diesen Code getestet, um sicherzustellen, dass jede Zeile 1 ergibt:

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
4
Jacob Eggers

Um alles zu multiplizieren, füge ich den Eintrag am Ende der Liste hinzu

    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
3
Scott Teuscher

Mit Abstand am schnellsten (Zeit ist im Vergleich zu Jacobs):

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
0
Versiera