ベクトル化
ベクトル化の使用
MATLAB® は、行列とベクトルに関する操作に最適化されています。ループベースでスカラー指向のコードを、MATLAB 行列とベクトル操作を使用するように変更するプロセスは "ベクトル化" と呼ばれます。コードのベクトル化の利点は次のとおりです。
外観: ベクトル化された数学的なコードは教科書の数式のように示されるので、コードが理解しやすくなります。
エラーの発生が少ない: ループがないため、ベクトル化コードはたいてい短くなります。コード行が少ないほど、プログラミング エラーが発生することも少なくなります。
パフォーマンス: 多くの場合、ベクトル化コードはループを含む同等のコードよりはるかに高速で実行されます。
一般計算のベクトル化コード
このコードは、0 ~ 10 の範囲で 1,001 個の値の正弦値を計算します。
i = 0; for t = 0:.01:10 i = i + 1; y(i) = sin(t); end
これは、同じコードをベクトル化したものです。
t = 0:.01:10; y = sin(t);
2 番目のコード サンプルは通常、最初のコードよりも高速で実行されるので、MATLAB をより効率的に使用できます。表示されているコードを含んだスクリプトを作成してシステム上で実行速度をテストしてから、関数 tic
と toc
を使用して実行時間を測定します。
特定タスクのベクトル化コード
このコードは、5 番目の要素ごとにベクトルの累積和を計算します。
x = 1:10000; ylength = (length(x) - mod(length(x),5))/5; y(1:ylength) = 0; for n= 5:5:length(x) y(n/5) = sum(x(1:n)); end
ベクトル化を使用すると、これよりもはるかに簡潔な MATLAB プロセスを記述できます。このコードは、タスクを達成する 1 つの方法を示します。
x = 1:10000; xsums = cumsum(x); y = xsums(5:5:length(x));
配列演算
配列演算は、データ セットのすべての要素に同じ演算を実行します。このタイプの演算は計算を反復して行う場合に便利です。たとえば、さまざまな円錐の直径 (D
) と高さ (H
) を記録してその体積 (V
) を収集するとします。円錐の情報を 1 つだけ収集する場合、その 1 つの円錐の体積を計算できます。
V = 1/12*pi*(D^2)*H;
次は、10,000 個の円錐の情報を収集する場合です。ベクトル D
と H
はそれぞれ 10,000 の要素を含んでおり、10,000 の体積を計算します。多くのプログラミング言語では、この MATLAB コードと同様のループを設定する必要があります。
for n = 1:10000 V(n) = 1/12*pi*(D(n)^2)*H(n); end
MATLAB では、スカラーの場合と同様の構文を使ってベクトルの各要素の計算を実行できます。
% Vectorized Calculation
V = 1/12*pi*(D.^2).*H;
メモ
演算子 *
、/
、^
の前にピリオド (.
) を入れると、配列演算子に変換されます。
配列演算子を使用して異なる次元の行列を組み合わせることもできます。サイズ 1 の次元に対するこの自動拡張は、グリッド作成のベクトル化、行列およびベクトルの演算などに便利です。
行列 A
はテストの点数を表し、行でさまざまなクラスを示すものとします。各クラスの平均点と個人の点数の差を計算する場合、ループを使用すると、演算は次のようになります。
A = [97 89 84; 95 82 92; 64 80 99;76 77 67;... 88 59 74; 78 66 87; 55 93 85]; mA = mean(A); B = zeros(size(A)); for n = 1:size(A,2) B(:,n) = A(:,n) - mA(n); end
これをより直接的に行うために、A - mean(A)
を使用してループを回避し、大幅に高速化する方法があります。
devA = A - mean(A)
devA = 18 11 0 16 4 8 -15 2 15 -3 -1 -17 9 -19 -10 -1 -12 3 -24 15 1
A
は 7 行 3 列の行列であり、mean(A)
は 1 行 3 列のベクトルですが、MATLAB は、行列と同じサイズをもつかのようにベクトルを暗黙的に拡張し、演算は通常の要素単位のマイナス演算として実行されます。
オペランドのサイズ要件では、各次元で配列が同じサイズであるか、いずれかが 1 でなければなりません。この要件が満たされると、いずれかの配列のサイズが 1 の次元が、もう一方の配列の対応する次元と同じサイズに拡張されます。詳細については、基本的な演算で互換性のある配列サイズを参照してください。
暗黙的な拡張は、多次元データを使用している場合にもベクトル化に役立ちます。2 つの変数 x
および y
をもつ関数 F
を評価するとします。
F(x,y) = x*exp(-x2 - y2)
この関数を x
と y
ベクトルの点の各組み合わせで評価するには、値のグリッドを定義しなければなりません。このタスクでは、ループを使用した点の組み合わせによる反復は避けてください。代わりに、いずれかのベクトルが列で、もう一方が行の場合、ベクトルが x+y
または x-y
などの配列演算子で使用されると MATLAB は自動的にグリッドを作成します。この例では、x
が 21 行 1 列のベクトル、y
が 1 行 16 列のベクトルなので、x
の 2 番目の次元と y
の最初の次元を拡張することで、演算は 21 行 16 列の行列を生成します。
x = (-2:0.2:2)'; % 21-by-1 y = -1.5:0.2:1.5; % 1-by-16 F = x.*exp(-x.^2-y.^2); % 21-by-16
logical 配列演算
配列のバルク処理を論理拡張したものが、比較と判定のベクトル化です。MATLAB 比較演算子はベクトル入力を受け取り、ベクトル出力を返します。
たとえば、10,000 の円錐からデータを収集中に負の直径の値をいくつか記録したとします。>=
演算子を使って、ベクトルのどの値が有効な値かを判定できます。
D = [-0.2 1.0 1.5 3.0 -1.0 4.2 3.14]; D >= 0
ans = 0 1 1 1 0 1 1
D
の要素が非負である有効な円錐体積 Vgood
を選別できます。Vgood = V(D >= 0);
MATLAB では、関数 all
および any
をそれぞれ使用して、ベクトル全体の要素に論理 AND または論理 OR を実行できます。D
のすべての値が 0 未満の場合、警告をスローできます。
if all(D < 0) warning('All values of diameter are negative.') return end
MATLAB では、サイズに互換性のある 2 つのベクトルを比較できるので、さらに制限を設けることができます。このコードは、V が非負で D
が H
より大きいすべての値を検出します。
V((V >= 0) & (D > H))
比較を支援するため、MATLAB には Inf
や NaN
など、オーバーフロー、アンダーフロー、未定義演算子を示す特別な値があります。論理演算子 isinf
および isnan
は、これらの特別な値で論理テストを実行するのに役立ちます。たとえば、NaN
値を計算から除外した方が便利なことがよくあります。
x = [2 -1 0 3 NaN 2 NaN 11 4 Inf]; xvalid = x(~isnan(x))
xvalid = 2 -1 0 3 2 11 4 Inf
メモ
Inf == Inf
は真を返しますが、NaN == NaN
は常に偽を返します。
行列演算
コードをベクトル化するとき、行列を特定のサイズまたは構造で作成しなければならない場合があります。一様の行列を作成する手法があります。たとえば、同じ要素をもつ 5 行 5 列の行列が必要だとします。
A = ones(5,5)*10;
v = 1:5; A = repmat(v,3,1)
A = 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
関数 repmat
は、小さい行列またはベクトルから行列を作成する柔軟性をもち合わせています。repmat
は入力行列を反復して行列を作成します。
A = repmat(1:3,5,2) B = repmat([1 2; 3 4],2,2)
A = 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 B = 1 2 1 2 3 4 3 4 1 2 1 2 3 4 3 4
演算の順序、設定、カウント
多くのアプリケーションでは、ベクトル上の要素に行う計算は同じベクトル内の他の要素に依存します。たとえば、ベクトル x はセットを表すかもしれません。for
ループまたは while
ループを使用せずにセット内を反復する処理は、わかりにくくなります。ベクトル化コードを使用すると、プロセスはより明確になり、構文はより単純になります。
冗長要素の除外
ベクトルの冗長要素を見つけるには、さまざまな方法があります。1 つの方法は、関数 diff
を使用することです。ベクトル要素を並べ替えた後、そのベクトル上で関数 diff
を使用する場合、隣接する要素が等しいとゼロ要素が生成されます。diff(x)
は x
より 1 つ少ない要素をもつベクトルを生成するので、他のどの要素とも等しくない要素をセットに追加しなければなりません。NaN
は常にこの条件を満たします。最後に、論理インデックス付けを使用してセット内の一意の要素を選別します。
x = [2 1 2 2 3 1 3 2 1 3]; x = sort(x); difference = diff([x,NaN]); y = x(difference~=0)
y = 1 2 3
unique
を使用しても同じ演算を行うことができます。y=unique(x);
unique
には必要以上の機能が備えられているため、コード実行速度が遅くなる可能性があります。各コード スニペットのパフォーマンスを測定する場合は、関数 tic
と toc
を使用します。ベクトル内での要素のカウント
x
のセットまたはサブセットを返すだけではなく、ベクトル内のある要素の出現回数をカウントできます。ベクトルを並べ替えた後、関数 find
を使用して diff(x)
でのゼロ値のインデックスを判定し、要素の値が変化した場所を示すことができます。関数 find
から出力された後続のインデックス間の差は、特定要素の出現数を示します。
x = [2 1 2 2 3 1 3 2 1 3]; x = sort(x); difference = diff([x,max(x)+1]); count = diff(find([1,difference])) y = x(find(difference))
count = 3 4 3 y = 1 2 3
find
は NaN
要素のインデックスを返しません。関数 isnan
と isinf
を使用して、NaN
および Inf
値の数をカウントできます。count_nans = sum(isnan(x(:))); count_infs = sum(isinf(x(:)));
ベクトル化でよく使用する関数
関数 | 説明 |
---|---|
all | すべての配列要素が非ゼロか true かを判定 |
any | 任意の配列要素が非ゼロかどうかを判断 |
cumsum | 累積和 |
diff | 差分と近似導関数 |
find | 非ゼロ要素のインデックスと値を見つける |
ind2sub | 線形インデックスから添字を抽出 |
ipermute | N 次元配列の次元の再配列を逆操作 |
logical | 数値を logical に変換 |
meshgrid | 2 次元および 3 次元の空間の四角形グリッド |
ndgrid | N 次元空間での四角形のグリッド |
permute | 多次元配列の次元の再配列 |
prod | 配列要素の積 |
repmat | 配列のコピーの繰り返し |
reshape | 配列の形状変更 |
shiftdim | 次元のシフト |
sort | 配列要素の並べ替え |
squeeze | 大きさが 1 の次元の削除 |
sub2ind | 添字から線形インデックスへの変換 |
sum | 配列要素の和 |