web-dev-qa-db-ja.com

MATLABでベクトルを効率的に正規化する方法は?関連する組み込み関数はありますか?

MATLABで次のようにベクトルVを正規化します。

normalized_V = V/norm(V);

しかし、MATLABでベクトルを正規化する最もエレガントな(効率的な)方法ですか?

47
Kamran Bigdely

提案する元のコードが最善の方法です。

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は、最初の呼び出しで重要なキャッシュペナルティがないことを確認するために、ここで2回計算されます。

ここでのタイミング情報は、Windows上のR2008a x64で生成されました。


編集:

ノービスの提案に基づいて修正された回答(コメントを参照)。マトリックス数学(ほとんど)が勝ちます:

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

私見では、「norm(V)」と「sqrt(V '* V)」の違いは十分に小さいため、ほとんどのプログラムでは、より明確なものを使用するのが最善です。私にとって、「norm(V)」はより明確で読みやすいですが、Matlabでは「sqrt(V '* V)」は今でも慣用的です。

41
Mr Fooz

私はMATLABを知らず、それを使用したことはありませんが、あなたは分割しているように思えます。どうして?このようなものははるかに高速になります:

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

遭遇する唯一の問題は、[〜#〜] 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's basically 0
end

プログラムでそれが必要な場合、通常はunitと呼ばれる独自の関数に上記のコードを入れます(基本的にベクトルを単位に変えるため)同じ方向を指すベクトル)。

9
gnovice

Fooz氏のコードを取り、Arlenのソリューションも追加しました。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
4
Jacob Eggers

すべてを乗算するという合理性により、リストの最後にエントリを追加します

    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

最速(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