添字表記法

数学およびプログラミングにおける添字表記法（そえじひょうきほう、英: index notation; 指数記法）あるいは添字記法とは、行列のような配列の特定の要素を示すために用いられる記法である。添字の用い方はそれを与える対象によって異なる。リスト、ベクトル、行列などデータ構造の違いによって、あるいは数学の論文を書くか、計算機のプログラムを書くかによってもその用法は異なる。 数学においては、配列の要素を下付きの添字によって示すことがしばしば行われる。添字には整数の定数や変数が用いられる。この場合、特に添数（てんすう）とも呼ぶ。配列は一般にはテンソルの形をとり、これは多次元の配列として扱うことができる。より親しみ深い例としては、ベクトル（1 次元配列）や行列（2 次元配列）が挙げられる。これらはテンソルの特殊な例である。 以下では、ベクトルや行列、より一般のテンソルに関する記法の基本的な考えを紹介する。 ベクトルは数の並びとして扱うことができ、行ベクトルまたは列ベクトルで表現される（どちらの表現をとるかは簡便さや文脈に依存する）。 a = ( a 1 a 2 ⋮ a n ) , a = ( a 1 a 2 ⋯ a n ) {\displaystyle \mathbf {a} ={\begin{pmatrix}a_{1}\\a_{2}\\\vdots \\a_{n}\end{pmatrix}},\quad \mathbf {a} ={\begin{pmatrix}a_{1}&a_{2}&\cdots &a_{n}\end{pmatrix}}} この場合、指数記法を用いることで、添字の i が 1 から n までを走ることが既知とする限りにおいて、配列の要素を総称的に ai とだけ書くことができる 。 たとえば次のようなベクトルが与えられた場合、 a = ( 10 8 9 6 3 5 ) {\displaystyle \mathbf {a} ={\begin{pmatrix}10&8&9&6&3&5\end{pmatrix}}} それぞれの成分は、次のように表すことができる。 a 1 = 10 , a 2 = 8 , … , a 6 = 5 {\displaystyle a_{1}=10,\,a_{2}=8,\,\dots ,a_{6}=5} . この記法は数学や物理学におけるベクトルに対して適用できる。ベクトル方程式 a + b = c {\displaystyle \mathbf {a} +\mathbf {b} =\mathbf {c} } は、添字は予め与えられた範囲の値を取ることを前提にして、これらのベクトルの成分を用いて a i + b i = c i {\displaystyle a_{i}+b_{i}=c_{i}} と書くことができる。この式は、各添字に対して一つずつ与えられる、成分の間の方程式の集合を表している。各ベクトルが n 個の成分を持つならば、添字の範囲は i = 1, 2, ..., n で、上式の表す方程式の集合は明示的には a 1 + b 1 = c 1 a 2 + b 2 = c 2 ⋮ a n + b n = c n {\displaystyle {\begin{aligned}a_{1}+b_{1}&=c_{1}\\a_{2}+b_{2}&=c_{2}\\&\;\,\vdots \\a_{n}+b_{n}&=c_{n}\end{aligned}}} を意味する。つまり、添字表記法は 一般的な構造を一つの方程式に表しつつ その一方で、各成分に対して適用できる という意味で効率の良い省略記法を提供するのである。 1 つより多くの添字を用いる配列は、行列の成分など多次元の配列要素を表すことに用いられる（図を参照）。 A = ( a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋮ ⋮ ⋱ ⋮ a m 1 a m 2 ⋯ a m n ) {\displaystyle \mathbf {A} ={\begin{pmatrix}a_{11}&a_{12}&\cdots &a_{1n}\\a_{21}&a_{22}&\cdots &a_{2n}\\\vdots &\vdots &\ddots &\vdots \\a_{m1}&a_{m2}&\cdots &a_{mn}\end{pmatrix}}} 行列 A の成分は 2 つの添字を用いて表される。2 つの添字 i, j の間には、ai,j のようにコンマで区切りが挿れられる場合もあれば、aij のようにそうでない場合もある。最初の添字 i が行の番号を表し、2つ目の添字 j が列の番号を表している。コンマを挿れない書き方では、ij を i と j の積と勘違いしないように注意すべきである。 具体例を挙げると、以下の行列が与えられたとき、 A = ( 9 8 6 1 2 7 4 9 2 6 0 5 ) {\displaystyle \mathbf {A} ={\begin{pmatrix}9&8&6\\1&2&7\\4&9&2\\6&0&5\end{pmatrix}}} この行列の各成分は次のような対応関係を持つ。 a 11 = 9 , a 12 = 8 , a 21 = 1 , … , a 23 = 7 , … . {\displaystyle a_{11}=9,\,a_{12}=8,a_{21}=1,\,\dots ,\,\,a_{23}=7,\,\dots .} ベクトルと同様に以下の様な行列方程式を考えることができる。 A + B = C . {\displaystyle \mathbf {A} +\mathbf {B} =\mathbf {C} .} これを各成分について書けば、すべての i および j について、 A i j + B i j = C i j {\displaystyle A_{ij}+B_{ij}=C_{ij}} となる。それぞれの行列が m 行 n 列であった場合、添え字は i = 1, 2, ..., m, j = 1, 2, ..., n の範囲を動き、方程式の数は m×n 個になる。 添字表記法をより一般化すればテンソルを扱えるようになる。たとえばテンソル方程式は以下のように示される。 A i 1 i 2 ⋯ + B i 1 i 2 ⋯ = C i 1 i 2 ⋯ . {\displaystyle A_{i_{1}i_{2}\cdots }+B_{i_{1}i_{2}\cdots }=C_{i_{1}i_{2}\cdots }.} テンソル解析においては共変成分と反変成分を区別するために、上付き添字と下付き添字が使い分けられる。 様々なプログラミング言語において、配列要素の参照に添字表記法が用いられる。添字表記法はアセンブリ言語の実装に近い形で用いられ、配列の先頭要素のアドレスを基準とし、配列内の要素のアドレスは添字の指数と配列要素のサイズ配列要素のサイズの積によって指定される。 例えば、整数型の配列を格納するための領域のアドレスが 0x3000 から始まったとすると、配列の基準アドレスは 0x3000 であり、整数型のデータを表現するのに 4 バイトの領域が必要だとすれば、各配列要素のアドレスは基準アドレスに整数型のサイズの倍数を足して 0x3000, 0x3004, 0x3008, ..., 0x3000 + 4(n - 1) と割り振られる。より一般的には、データ型のサイズが s の配列 b の i 番の配列要素のアドレスは、b + is と表すことができる。 C言語では上述の配列を、添字演算子 [] を用いて base[index] と表すか、あるいは間接参照演算子 * を用いて *(base + index) と表すことができる。C言語の標準的な実装では、添字演算子による表現も間接参照演算子による表現も全く等価であり、前者の添字演算子による参照は後者のアドレスと間接参照演算子を用いた形に変換される。間接参照による表現 *(base + index) を見れば明らかなように、base と index は交換可能である。したがって、index[base] と base[index] は等価である。 C++など他のプログラミング言語においても、配列のようなコンテナをC言語と同様に添字演算子を用いて表現することがあるが、添字演算子の定義は上述のC言語におけるものと必ずしも同一でなく、特に間接参照演算子やアドレス演算子 & との組み合わせについて注意を要する。何故ならC++等では、既述の演算子の意味が、文脈によりC言語での意味とは変わる事があるため。たとえば単純な配列では添字演算子の左右のオペランドは交換可能だが、通常用いられる多くのコンテナにおいては交換可能ではない。まず、一般には *base は base[0] と等価ではなく、base は &base[0] と等価ではない。また、素朴な配列では base 自身がデータ領域の先頭アドレスを指すポインタ変数として扱われ、整数との加算が定義されたイテレータの役割を果たすが、一般のコンテナでは base はイテレータ型でないかイテレータであっても整数との加算が定義されていないため、*(base + index) という記述も未定義である。 複数の添字を持つ配列を利用すれば、2 次元の表などを表現することも可能になる。そのような配列を作るには次の 3 通りの方法が考えられる。 2 次元配列を 1 次元の配列として作る（1 次元配列の各要素はそのまま 2 次元配列の要素に対応する）：elementType array[size of array]; 1 次元配列の各要素として別の配列を持たせる（つまり、配列の配列を作る）：elementType array[# of rows][# of cols]; 配列の行を 1 次元配列として確保し、それぞれの行を参照するための配列を別に用意する：elementType *array[# of rows]; C言語ではこれら 3 つの方法すべてが利用可能である。 最初の方法は、プログラマが計算機のメモリへの配列の格納方法を決め、各要素を参照するための関係式を与えることによって行われる。たとえば行の要素数が N 個の配列については i*N + j という形で指数を与え、j のとり得る値の範囲を 0 から N - 1 に制限すればよい。 2 つ目の方法は、各行の要素数がすべて同じであることがコードを書く時点で分かっているような場合に用いられる。プログラマは配列の列数だけを指示すればよく、3列の配列を使う場合、ElementType tableName[][3]; と指定する。この配列の特定の要素を参照する場合には tablename[first index][second index] と書けばよい。コンパイラは各行が占有するメモリ領域の合計を計算し、第一の添字から要求された行のアドレスを探し、第二の添字によってその行の指定された要素のアドレスを探し出す。 3 つ目の方法を用いる場合、elementType *tableName[]; のように配列の要素がポインターとなるように宣言する。プログラマが特定の要素を参照するためには tablename[first index][second index] と書けばよく、コンパイラは最初の添字によって指定された行のアドレスを参照し、そのアドレスを用いて第二の添字から指定された要素のアドレスを計算するような指示を生成する。 3×3 の行列 A, B の積を result に格納する関数。 void mult3x3f(float result[][3], const float A[][3], const float B[][3]) { int i, j, k; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { result[i][j] = 0; for (k = 0; k < 3; ++k) { result[i][j] += A[i][k] * B[k][j]; } } } } Fortran や Pascal のような他の言語では（特に指定のない限り）添字の指数は 1 から始まる。メモリ領域への指数の割り当ては、指数を 1 から始める方法に合うように変更することができ、この場合には i 番目の要素のメモリ上の位置は、配列の基準アドレスを b, 配列要素 1 つを表すためのメモリ領域の大きさを s とすれば、b + (i − 1)s と表すことができる。たとえば配列の先頭アドレスに対応する指数は i = 1 だから、アドレスは b となる。