Inner関数の働き・役割
Inner関数は、2つの行列の内積を計算するために使用されます。行列の内積とは、行列の各行と別の行列の各行の間でベクトルのドット積(要素ごとの積の合計)を計算して新しい行列を作る操作です。
例えば、行列Aが2行3列、行列Bが3行3列のとき、それぞれの行と行でドット積を計算し、結果を行列Cとして返します。数学では、行列の積と呼ばれるこの操作をMQL5ではInner関数を使って行います。
ドット積とは
行列Aと行列Bの内積の結果となる行列Cの要素は、Aの行とBの行のベクトルのドット積です。ドット積は、2つの同じサイズのベクトルの各要素を掛け合わせて合計を求める計算です。
Inner関数の引数について
matrix matrix::Inner(
const matrix& b // 2番目の行列
);
Inner関数は、2つの行列の内積を計算するために、1つの引数を受け取ります。
第1引数:b
bは「2番目の行列」を意味します。最初の行列は、Inner関数を呼び出すオブジェクトとして既に指定されているため、追加でbのみを指定します。
bはmatrix型の引数です。matrix型は、複数の要素(数値)を2次元配列の形式で格納したデータ構造です。
ここで注意すべき点は、最初の行列(呼び出し元)とbの行と列の対応関係です。Inner関数を正しく動作させるためには、次の条件が必要です。
これにより、内積を計算するための要件が満たされます。
Inner関数の戻り値について
Inner関数の戻り値はmatrix型の行列です。この戻り値の行列は、次の要素を持っています。
各要素は、呼び出し元の行列の各行と2番目の行列の各行とのドット積によって計算されます。
Inner関数を使ったサンプルコード
// スクリプトが実行されたときに自動的に呼び出されるイベントハンドラー
void OnStart()
{
// --- 行列A(2行3列)の定義 ---
// 行列Aは2行3列のデータを持つ行列です
// 行列Aの内容:0, 1, 2 と 3, 4, 5
matrix a = {{0,1,2},{3,4,5}};
// --- 行列B(3行3列)の定義 ---
// 行列Bは3行3列のデータを持つ行列です
// 行列Bの内容:0,1,2、3,4,5、6,7,8 の3つの行
matrix b = {{0,1,2},{3,4,5},{6,7,8}};
// --- Inner関数による行列の内積を計算 ---
// a.Inner(b) は、行列aと行列bの内積を計算し、結果を新しい行列cに代入します
// aは2行3列、bは3行3列なので、結果の行列cは2行3列になります
matrix c = a.Inner(b);
// --- 結果の行列cをエキスパートログに出力 ---
// Print関数は結果をエキスパートログに表示します
// 出力される内容は、行列cの各要素です
Print(c); // 結果:[[5,14,23], [14,50,86]]
// --- 行列A1(1行3列)の定義 ---
// 行列a1は1行3列のデータを持つ行列です
// 行列A1の内容:0, 1, 2
matrix a1 = {{0,1,2}};
// --- Inner関数による行列A1と行列Bの内積を計算 ---
// a1.Inner(b) は、行列a1(1行3列)と行列b(3行3列)の内積を計算します
// 結果の行列c1は1行3列になります
matrix c1 = a1.Inner(b);
// --- 結果の行列c1をエキスパートログに出力 ---
// 出力される内容は、行列c1の各要素です
Print(c1); // 結果:[[5,14,23]]
}
コードの解説
サンプル行列
以下の2つの行列を考えます:
行列A(2行3列):
a = [[0,1,2], [3,4,5]]
行列B(3行3列):
b = [[0,1,2], [3,4,5], [6,7,8]]
Inner関数の動作
a.Inner(b) の処理では、行列aの各行と行列bの各行の間でベクトルのドット積が計算されます。
計算過程
C[0][0](Aの1行目 と Bの1行目 のドット積)
C[0][0] = (a[0][0]*b[0][0]) + (a[0][1]*b[0][1]) + (a[0][2]*b[0][2])
C[0][0] = (0*0) + (1*1) + (2*2) = 0 + 1 + 4 = 5
C[0][1](Aの1行目 と Bの2行目 のドット積)
C[0][1] = (a[0][0]*b[1][0]) + (a[0][1]*b[1][1]) + (a[0][2]*b[1][2])
C[0][1] = (0*3) + (1*4) + (2*5) = 0 + 4 + 10 = 14
C[0][2](Aの1行目 と Bの3行目 のドット積)
C[0][2] = (a[0][0]*b[2][0]) + (a[0][1]*b[2][1]) + (a[0][2]*b[2][2])
C[0][2] = (0*6) + (1*7) + (2*8) = 0 + 7 + 16 = 23
C[1][0](Aの2行目 と Bの1行目 のドット積)
C[1][0] = (a[1][0]*b[0][0]) + (a[1][1]*b[0][1]) + (a[1][2]*b[0][2])
C[1][0] = (3*0) + (4*1) + (5*2) = 0 + 4 + 10 = 14
C[1][1](Aの2行目 と Bの2行目 のドット積)
C[1][1] = (a[1][0]*b[1][0]) + (a[1][1]*b[1][1]) + (a[1][2]*b[1][2])
C[1][1] = (3*3) + (4*4) + (5*5) = 9 + 16 + 25 = 50
C[1][2](Aの2行目 と Bの3行目 のドット積)
C[1][2] = (a[1][0]*b[2][0]) + (a[1][1]*b[2][1]) + (a[1][2]*b[2][2])
C[1][2] = (3*6) + (4*7) + (5*8) = 18 + 28 + 40 = 86
計算結果
最終的な行列Cの内容は次のようになります:
C = [[5, 14, 23],
[14, 50, 86]]
1行の行列A1と行列Bの内積
行列A1(1行3列)と行列B(3行3列)を掛け合わせる場合も、A1の行とBの各行のドット積が計算されます。
C1[0][0](A1の1行目 と Bの1行目)
C1[0][0] = (0*0) + (1*1) + (2*2) = 0 + 1 + 4 = 5
C1[0][1](A1の1行目 と Bの2行目)
C1[0][1] = (0*3) + (1*4) + (2*5) = 0 + 4 + 10 = 14
C1[0][2](A1の1行目 と Bの3行目)
C1[0][2] = (0*6) + (1*7) + (2*8) = 0 + 7 + 16 = 23
結果の行列C1
C1 = [[5, 14, 23]]
まとめ
Inner関数は、2つの行列の行と行のドット積を計算して、新しい行列を作成します。
結果として得られる行列の要素は、それぞれのドット積の計算結果です。
補足:各行同士のドット積を計算し、結果の行列に格納するオリジナル関数
組み込み関数であるInner関数を使わずに、Inner関数と同じような処理を行う場合、以下のような関数定義になります。
// MatrixInner関数
// 2つの行列aとbの各行同士のドット積を計算し、結果の行列cに格納する関数
// 計算が正常に完了すればtrueを返し、サイズの不一致などで失敗した場合はfalseを返します
bool MatrixInner(matrix& c, const matrix& a, const matrix& b)
{
// --- 列の数が等しいかを確認 ---
// 行列aと行列bの列数が一致しない場合は、内積の計算ができないためfalseを返して終了します
if(a.Cols() != b.Cols())
return(false); // 列数が一致しないので処理を中断しfalseを返す
// --- 結果の行列のサイズを決定 ---
// rows: 結果行列の行数(行列aの行数に依存します)
// cols: 結果行列の列数(行列bの行数に依存します)
ulong rows = a.Rows(); // 行列aの行数を取得
ulong cols = b.Rows(); // 行列bの行数を取得
// --- 結果の行列を初期化 ---
// 結果行列resultは、rows行cols列の行列になります
matrix result(rows, cols);
// --- 行列aの各行と行列bの各行のドット積を計算 ---
// 行列aの行を順に取り出し、行列bの各行とドット積を計算してresultに格納します
for(ulong i = 0; i < rows; i++) // 行列aの行数分繰り返す(外側のループ)
{
// v1: 行列aのi行目をベクトルとして取得
vector v1 = a.Row(i); // 行列aのi行目を取得
for(ulong j = 0; j < cols; j++) // 行列bの行数分繰り返す(内側のループ)
{
// v2: 行列bのj行目をベクトルとして取得
vector v2 = b.Row(j); // 行列bのj行目を取得
// --- ドット積を計算して結果に格納 ---
// v1.Dot(v2): v1とv2のドット積を計算する
// 計算結果をresultのi行j列に格納します
result[i][j] = v1.Dot(v2);
}
}
// --- 結果を出力変数cに代入 ---
// 計算が完了した結果の行列resultをcに代入します
c = result;
// --- 処理が正常に完了したことを示すtrueを返す ---
return(true);
}
MatrixInner関数の解説
関数の概要
MatrixInner関数は、2つの行列aとbの「行」と「行」をドット積で計算し、その結果を新しい行列として返す関数です。
ここでの「ドット積」とは、2つのベクトル(数値の並び)を掛け合わせて合計する計算のことです。
この関数は入れ子状のforループ(ループの中にループがある構造)を使用して、行列aの各行と行列bの各行を順番に掛け合わせ、計算結果を行列cに格納しています。
関数の引数と戻り値
処理の流れ
1. 列数の確認
最初にif(a.
Cols() != b.Cols())で、行列aと行列bの列数が一致しているかを確認します。
2. 結果行列のサイズを決定
3. 入れ子のforループで行と行のドット積を計算
外側のforループ:行列aの行を順番に取り出します(i
は行番号を示す)。
内側のforループ:行列bの行を順番に取り出します(j
は行番号を示す)。
- a.Row(i)で行列aのi行目をベクトルとして取得します(変数名:
v1
)。 - b.Row(j)で行列bのj行目をベクトルとして取得します(変数名:
v2
)。 - v1.Dot(v2)を使って、v1とv2のドット積を計算します。
この結果を、行列cのi行j列に格納します。
具体的な数字をあてはめた計算の流れ
次の2つの行列aとbを例に考えます。
行列a(2行3列):
a = [[0,1,2],
[3,4,5]]
行列b(3行3列):
b = [[0,1,2],
[3,4,5],
[6,7,8]]
この場合、結果の行列cは「2行3列」になります。
ステップ1:外側のforループ(iの遷移)
ステップ2:内側のforループ(jの遷移)
i=0のとき(行列aの1行目)
j=0(行列bの1行目)
C[0][0] = (a[0][0]*b[0][0]) + (a[0][1]*b[0][1]) + (a[0][2]*b[0][2])
C[0][0] = (0*0) + (1*1) + (2*2) = 0 + 1 + 4 = 5
j=1(行列bの2行目)
C[0][1] = (a[0][0]*b[1][0]) + (a[0][1]*b[1][1]) + (a[0][2]*b[1][2])
C[0][1] = (0*3) + (1*4) + (2*5) = 0 + 4 + 10 = 14
j=2(行列bの3行目)
C[0][2] = (a[0][0]*b[2][0]) + (a[0][1]*b[2][1]) + (a[0][2]*b[2][2])
C[0][2] = (0*6) + (1*7) + (2*8) = 0 + 7 + 16 = 23
結果の1行目:[5, 14, 23]
i=1のとき(行列aの2行目)
j=0(行列bの1行目)
C[1][0] = (a[1][0]*b[0][0]) + (a[1][1]*b[0][1]) + (a[1][2]*b[0][2])
C[1][0] = (3*0) + (4*1) + (5*2) = 0 + 4 + 10 = 14
j=1(行列bの2行目)
C[1][1] = (a[1][0]*b[1][0]) + (a[1][1]*b[1][1]) + (a[1][2]*b[1][2])
C[1][1] = (3*3) + (4*4) + (5*5) = 9 + 16 + 25 = 50
j=2(行列bの3行目)
C[1][2] = (a[1][0]*b[2][0]) + (a[1][1]*b[2][1]) + (a[1][2]*b[2][2])
C[1][2] = (3*6) + (4*7) + (5*8) = 18 + 28 + 40 = 86
結果の2行目:[14, 50, 86]
最終結果の行列C
C = [[5, 14, 23],
[14, 50, 86]]
まとめ
MatrixInner関数は、行列aの各行と行列bの各行を順番にドット積で計算し、結果を新しい行列cに格納します。