web-dev-qa-db-fra.com

Comment normaliser efficacement un vecteur dans MATLAB? Toute fonction intégrée associée?

Je normalise un vecteur V dans MATLAB comme suit:

normalized_V = V/norm(V);

cependant, est-ce la manière la plus élégante (efficace) de normaliser un vecteur dans MATLAB?

45
Kamran Bigdely

Le code original que vous suggérez est le meilleur moyen.

Matlab est extrêmement doué pour les opérations vectorisées telles que celle-ci, du moins pour les grands vecteurs.

La fonction de norme intégrée est très rapide. Voici quelques résultats de chronométrage:

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

La V1 est calculée une seconde fois ici pour s’assurer qu’il n’y a pas de pénalité de cache importante lors du premier appel.

Les informations de minutage ici ont été produites avec R2008a x64 sous Windows.


MODIFIER:

Réponse révisée sur la base des suggestions de gnovice (voir commentaires). Matrix Math (à peine) gagne:

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, la différence entre "norme (V)" et "sqrt (V '* V)" est suffisamment petite pour que pour la plupart des programmes, il vaut mieux choisir celui qui est plus clair. Pour moi, "norme (V)" est plus clair et plus facile à lire, mais "sqrt (V '* V)" est toujours idiomatique dans Matlab.

39
Mr Fooz

Je ne connais aucun MATLAB et je ne l'ai jamais utilisé, mais il me semble que vous vous divisez. Pourquoi? Quelque chose comme ça sera beaucoup plus rapide:

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

Le seul problème que vous rencontrerez est si la norme de V est égale à zéro (ou très proche de celle-ci). Cela pourrait vous donner Inf ou NaN lorsque vous divisez, avec un avertissement de division par zéro. Si vous ne souhaitez pas obtenir un Inf ou un NaN, vous pouvez simplement activer et désactiver l'avertissement à l'aide de WARNING :

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

Si vous ne voulez pas de valeurs Inf ou NaN, vous devez d'abord vérifier la taille de la norme:

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

Si j'en ai besoin dans un programme, je mets généralement le code ci-dessus dans ma propre fonction, généralement appelée unit (car il transforme un vecteur en un vecteur unitaire pointant dans la même direction).

9
gnovice

J'ai pris le code de M. Fooz et également ajouté la solution d'Arlen. Voici les temps que j'ai obtenus pour 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

Ensuite, à cause de quelque chose que je suis en train de regarder, j'ai testé ce code pour vérifier que chaque rangée vaut 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
4
Jacob Eggers

Par le rationnel de tout multiplier, j'ajoute l'entrée à la fin de la liste

    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

Le plus rapide de loin (le temps est en comparaison de 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