Main Content

ベクトル化

ベクトル化の使用

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 をより効率的に使用できます。表示されているコードを含んだスクリプトを作成してシステム上で実行速度をテストしてから、関数 tictoc を使用して実行時間を測定します。

特定タスクのベクトル化コード

このコードは、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 個の円錐の情報を収集する場合です。ベクトル DH はそれぞれ 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)

この関数を xy ベクトルの点の各組み合わせで評価するには、値のグリッドを定義しなければなりません。このタスクでは、ループを使用した点の組み合わせによる反復は避けてください。代わりに、いずれかのベクトルが列で、もう一方が行の場合、ベクトルが 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

明示的にグリッドを作成する場合は、関数 meshgridndgrid を使用できます。

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
MATLAB の論理インデックス付けを直接使用して、対応する 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 が非負で DH より大きいすべての値を検出します。

V((V >= 0) & (D > H))
結果のベクトルは入力と同じサイズになります。

比較を支援するため、MATLAB には InfNaN など、オーバーフロー、アンダーフロー、未定義演算子を示す特別な値があります。論理演算子 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 には必要以上の機能が備えられているため、コード実行速度が遅くなる可能性があります。各コード スニペットのパフォーマンスを測定する場合は、関数 tictoc を使用します。

ベクトル内での要素のカウント

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
関数 findNaN 要素のインデックスを返しません。関数 isnanisinf を使用して、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

配列要素の和

関連するトピック

外部の Web サイト