How to normalize a vector in MATLAB efficiently? A

2020-05-14 13:52发布

问题:

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 7 years ago.

I normalize a vector V in MATLAB as following:

normalized_V = V/norm(V);

however, is it the most elegant (efficient) way to normalize a vector in MATLAB?

回答1:

The original code you suggest is the best way.

Matlab is extremely good at vectorized operations such as this, at least for large vectors.

The built-in norm function is very fast. Here are some timing results:

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 is calculated a second time here just to make sure there are no important cache penalties on the first call.

Timing information here was produced with R2008a x64 on Windows.


EDIT:

Revised answer based on gnovice's suggestions (see comments). Matrix math (barely) wins:

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, the difference between "norm(V)" and "sqrt(V'*V)" is small enough that for most programs, it's best to go with the one that's more clear. To me, "norm(V)" is clearer and easier to read, but "sqrt(V'*V)" is still idiomatic in Matlab.



回答2:

I don't know any MATLAB and I've never used it, but it seems to me you are dividing. Why? Something like this will be much faster:

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


回答3:

The only problem you would run into is if the norm of V is zero (or very close to it). This could give you Inf or NaN when you divide, along with a divide-by-zero warning. If you don't care about getting an Inf or NaN, you can just turn the warning on and off using WARNING:

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

If you don't want any Inf or NaN values, you have to check the size of the norm first:

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

If I need it in a program, I usually put the above code in my own function, usually called unit (since it basically turns a vector into a unit vector pointing in the same direction).



回答4:

I took Mr. Fooz's code and also added Arlen's solution too and here are the timings that I've gotten for 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

Then, because of something I'm currently looking at, I tested out this code for ensuring that each row sums to 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


回答5:

By the rational of making everything multiplication I add the entry at the end of the list

    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


回答6:

Fastest by far (time is in comparison to 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