8.1.1 高度なインデックス作成
連鎖インデックス
Octave では、中間変数を使用せずに、繰り返し (連鎖) インデックス式を使用して、単一のコマンドで配列のサブセットを抽出できます。これにより、複雑なインデックス操作や複数のインデックス方法を使用するコードを簡単に記述できます。次の例は、2 つの同等のインデックス抽出操作を示しています。
A = reshape (1:16, 4, 4);
B = A(2:4, 2:3);
C = B(3:5);
D = C( [ true, false, true ] )
⇒ D = [ 8, 11 ]
D = A(2:4, 2:3)(3:5)([ true, false, true ])
⇒ D = [ 8, 11 ]
連鎖インデックスは、同じ結果を生成する単一のインデックス式よりも必然的に遅くなりますが、通常は、中間変数の割り当てを伴う複数の個別のインデックス操作を実行するよりも計算効率が高くなります。
連鎖インデックスは右側の式とのみ互換性があり、代入演算の左側では使用できません。
コンポーネントから線形インデックスへの変換
インデックスをコンポーネントの直積として記述できない配列からエントリのサブセットを抽出する必要がある場合は、関数とともに線形インデックスをsub2ind使用できます。例:
A = reshape (1:8, 2, 2, 2) # Create 3-D array A = ans(:,:,1) = 1 3 2 4 ans(:,:,2) = 5 7 6 8 A(sub2ind (size (A), [1, 2, 1], [1, 1, 2], [1, 2, 1])) ⇒ ans = [A(1, 1, 1), A(2, 1, 2), A(1, 2, 1)]
: ind = sub2ind (dims, i, j)
: ind = sub2ind (dims, s1, s2, …, sN)
下付き文字を線形インデックスに変換します。
入力dimsは次元ベクトルであり、各要素はそれぞれの次元の配列のサイズです (を参照size)。残りの入力は、変換される添字のスカラーまたはベクトルです。
出力ベクトルindには、変換された線形インデックスが含まれます。
背景: 配列要素は、1 から始まり配列内の要素数まで続く線形インデックスで指定することも、行、列、ページなどの添字で指定することもできます。関数と関数は、 ind2sub2sub2indつの形式を相互変換します。
線形インデックスは、すべての要素に番号が付けられるまで、次元 1 (行)、次元 2 (列)、次元 3 (ページ) などと走査します。次の 3 行 3 列の行列を考えてみましょう。
[(1,1), (1,2), (1,3)] [1, 4, 7] [(2,1), (2,2), (2,3)] ==> [2, 5, 8] [(3,1), (3,2), (3,3)] [3, 6, 9]
左の行列には、各行列要素の添字タプルが含まれています。右の行列には、同じ行列の線形インデックスが表示されます。
次の例は、 を 1 回呼び出すだけで、3 行 3 列の行列の2 次元インデックス (2,1)とを線形インデックスに変換する方法を示しています。 (2,3)sub2ind
s1 = [2, 2]; s2 = [1, 3]; ind = sub2ind ([3, 3], s1, s2) ⇒ ind = 2 8
See also: ind2sub, size.
: [s1, s2, …, sN] = ind2sub (dims, ind)
線形インデックスを下付き文字に変換します。
入力dimsは次元ベクトルであり、各要素はそれぞれの次元の配列のサイズです (を参照size)。2 番目の入力indには、変換される線形インデックスが含まれます。
出力s1、…、sNには変換された添え字が含まれます。
背景: 配列要素は、1 から始まり配列内の要素数まで続く線形インデックスで指定することも、行、列、ページなどの添字で指定することもできます。関数と関数は、 ind2sub2sub2indつの形式を相互変換します。
線形インデックスは、すべての要素に番号が付けられるまで、次元 1 (行)、次元 2 (列)、次元 3 (ページ) などと走査します。次の 3 行 3 列の行列を考えてみましょう。matrices:
[1, 4, 7] [(1,1), (1,2), (1,3)] [2, 5, 8] ==> [(2,1), (2,2), (2,3)] [3, 6, 9] [(3,1), (3,2), (3,3)]
左の行列には、各行列要素の線形インデックスが含まれています。右の行列には、同じ行列の添字タプルが表示されます。
次の例は、線形インデックス2を 83 行 3 列の行列の適切な下付き文字に変換する方法を示しています。
ind = [2, 8]; [r, c] = ind2sub ([3, 3], ind) ⇒ r = 2 2 ⇒ c = 1 3
出力添え字の数が次元の数を超える場合、超過した次元は に設定されます1。一方、次元よりも少ない添え字が指定されている場合は、超過した次元が最終的に要求された次元に結合されます。わかりやすくするために、次の例を検討してください。
ind = [2, 8]; dims = [3, 3]; ## same as dims = [3, 3, 1] [r, c, s] = ind2sub (dims, ind) ⇒ r = 2 2 ⇒ c = 1 3 ⇒ s = 1 1 ## same as dims = [9] r = ind2sub (dims, ind) ⇒ r = 2 8
See also: sub2ind, size.
: tf = isindex (ind)
: tf = isindex (ind, n)
indが有効なインデックスの 場合は true を返します。
有効なインデックスは、正の整数 (実数データ型の場合もあります) または論理配列のいずれかです。
nが存在する場合、インデックス付けする次元の最大範囲を指定します。可能な場合は、内部結果がキャッシュされるため、indを使用した後続のインデックス付けでは再度チェックが実行されません。
実装上の注意: 有効なインデックスのチェックが行われる前に、文字列はまず double 値に変換されます。文字列に NULL 文字 "\0" が含まれていない限り、常に有効なインデックスになります。
コンポーネント数が次元数と等しくない
' を含む配列と' 次元は 1 から ' までのインデックス式でインデックスできます。と' コンポーネント。通常最も一般的なケースでは、コンポーネントの数は 'ま' は次元の数と一致します 'とこの場合、通常のインデックス規則が適用され、各コンポーネントは配列のそれぞれの次元に対応します。
ただし、インデックス コンポーネントの数が次元の数を超える場合 ( M > nd)、超過分のコンポーネントはすべてシングルトン ( 1) である必要があります。さらに、 の場合、動作は、末尾の 次元を最後のインデックス次元 にM < ndマージするように入力オブジェクトを再形成することと同等です。したがって、結果は、元のオブジェクトではなく、インデックス式の次元を持ちます。これは、インデックスの次元が 1 より大きい場合 ( ) に常に当てはまり、線形インデックスの特別なルールは適用されません。これは、例を使用すると最も簡単に理解できます。 nd - MMM > 1
A = reshape (1:8, 2, 2, 2) # Create 3-D array A = ans(:,:,1) = 1 3 2 4 ans(:,:,2) = 5 7 6 8 ## 2-D indexing causes third dimension to be merged into second dimension. ## Equivalent array for indexing, Atmp, is now 2x4. Atmp = reshape (A, 2, 4) Atmp = 1 3 5 7 2 4 6 8 A(2,1) # Reshape to 2x4 matrix, second entry of first column: ans = 2 A(2,4) # Reshape to 2x4 matrix, second entry of fourth column: ans = 8 A(:,:) # Reshape to 2x4 matrix, select all rows & columns, ans = Atmp
ここで、関数の呼び出しを置き換えるために二重コロンをエレガントに使用していることに注意してください reshape。
配列のレプリケーション
線形インデックスのもう 1 つの高度な使用法は、単一の値で満たされた配列を作成することです。これは、スカラー値に 1 のインデックスを使用することで実行できます。結果は、インデックス式の次元を持ち、すべての要素が元のスカラーに等しいオブジェクトになります。たとえば、次のステートメントは、
a = 13; a(ones (1, 4))
4 つの要素がすべて 13 に等しい行ベクトルを生成します。
同様に、スカラーを2つの1のベクトルでインデックス付けすることで、行列を作成することができます。次のステートメント
a = 13; a(ones (1, 2), ones (1, 3))
すべての要素が13である2x3行列を作成します。これは次のようにも書けます。
13(ones (2, 3))
コード構築よりもインデックスを使用する方が、不要な乗算演算を回避できるため効率的です scalar * ones (M, N, …)。さらに、複製されるオブジェクトに対して乗算が定義されていない可能性がありますが、配列のインデックスは常に定義されています。次のコードは、それ自体がスカラーではない基本単位から 2x3 セル配列を作成する方法を示しています。
{"Hello"}(ones (2, 3))
(1 の行ベクトル)は範囲オブジェクト(増分 0)になることに注意してくださいones (1, n)。範囲は、開始値、増分、終了値、および値の合計数として内部的に保存されます。したがって、要素数が 4 を超える場合は、1 のベクトルまたは行列よりも保存が効率的です。特に、'r' は行ベクトルであり、式
r(ones (1, n), :) r(ones (n, 1), :)
結果は同じですが、少なくとも「r' そして 'ん' は十分に大きいです。最初のケースでは、インデックスは範囲として圧縮された形式で保持され、Octave は式を処理するためのより効率的なアルゴリズムを選択できます。
これらの手法に慣れていないユーザーに対する一般的な推奨事項は、repmatこのようなトリックを使用する、小さな配列を大きな配列に複製する関数を使用することです。
パフォーマンス向上のためのインデックス作成
インデックスの 2 つ目の用途は、コードの高速化です。インデックスは高速な操作であり、これを適切に使用することで、低速な操作である配列の個々の要素をループする必要性を減らすことができます。
値 a(i) = sqrt (i) を含む 10 要素の行ベクトル aを作成する次の例を考えます。
for i = 1:10 a(i) = sqrt (i); endfor
このようなループを使用してベクトルを作成するのは非常に非効率的です。この場合、次の式を使用する方がはるかに効率的です。
a = sqrt (1:10);
これによりループが完全に回避されます。
ループを回避できない場合や、複数の値を組み合わせて大きな行列を形成する必要がある場合は、最初に行列のサイズを設定(ストレージを事前割り当て)し、次にインデックスコマンドを使用して要素を挿入する方が一般的に高速です。たとえば、行列が与えられた場合a、
[nr, nc] = size (a); x = zeros (nr, n * nc); for i = 1:n x(:,(i-1)*nc+1:i*nc) = a; endfor
よりかなり速い
x = a; for i = 1:n-1 x = [x, a]; endfor
Octave は中間結果のサイズを繰り返し変更する必要がないためです。
パフォーマンス向上のさらなる提案については、 「ベクトル化とコード実行の高速化」を参照してください。