アルゴリズム
行列Aと行列Bの積は、以下の手順で行われる。
- 行列Aの行番号iを固定して列番号jを動かす。
- 行列Bの列番号countを固定して行番号jを動かす
プログラムを書くポイント
for文を二重ループにして計算することを考えると、
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
// ここの中身はiが固定されたまま、jが変化していく
}
}
このとき、計算するたびに動かしたい行列Aの列番号と行列Bの行番号はjにすることが考えられる。
また、行列Aの行番号はfor文の二重ループの順番通りにアクセスできれば良い。
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
printf("%d", matA[i][j]);
}
}
// 出力は、2×2の行列の場合
// matA[0][0]
// matA[0][1]
// matA[1][0]
// matA[1][1]
// の順に表示される
一方、行列Bの列番号については要素iとは別で固定してループさせる必要がある。
具体的には変数iを固定したまま、変数jのfor文を行列Bの列数分実行したい。
そこで、変数iを回すfor文の中に、変数jを回すfor文を内包するループを設ける。
プログラム
#include <stdio.h>
// # define N
int main(void){
// 計算する行列はここで定義
int N = 2; // 2×2の正方行列とする
int matA[2][2] = {{1, 2}, {1, 2}};
int matB[2][2] = {{3, 4}, {4, 1};
int element = 0; // 計算結果の各要素
// 以下のコメントアウトではi=0, j=0の時の計算を見てみる。
for(int i=0;i<N;i++){
int count = 0;
while(count<N){
element =0;
for (int j=0;j<N;j++){
/*
計算結果として出力したいのは、
i=0, j=0, count=0のとき
element += matA[0, 0]*matB[0, 0] = 1*3
i=0, j=1, count=0のとき
element += matA[0, 1]*matB[1, 0] = 2*4
*/
element += matA[i][j]*matB[j][count];
}
// 変数jのfor文を抜けたら出力
// 計算結果 element = 計算結果の行列[0][0] = 1*3+2*4 = 11
// ちなみに変数elementは計算結果の行列[i][count]に相当。
printf("%d\n", element);
count += 1;
}
}
}
解説
変数の値を書き下してみる。
i j count
0 0 0
0 1 0
0 0 1
0 1 1
1 0 0
1 1 0
1 0 1
1 1 1
この順番でfor文を回すと、変数iとjが行列Aの要素にアクセスする順番、
変数countと変数jが行列Bの要素にアクセスする順番になっていることが分かる。
改良
上のプログラムだとちょっとわかりにくいので、変数名をいじってもう少し分かりやすくする。
変数名は以下のように設定し直す。
- 変数countは名前がおかしいので、i, jの次のアルファベットであるkにする。
nrowA = the number of rows of matrix A
- 行列Aの行数:nrowA
- 行列Bの列数:ncolB (=nrowA)
- 行列Aの列数:ncolA
- 行列Bの行数:nrowB
- 計算結果の行列:matR
#include <stdio.h>
int main(void){
// 2×2の正方行列とする
int nrowA =2;
int ncolA= 2 ;
int nrowB = 2;
int matA[2][2] = {{1, 2}, {1, 2}};
int matB[2][2] = {{3, 4}, {4, 1}};
int matR[2][2];
for(int i=0;i<nrowA;i++){
for(int k=0;k<nrowB;k++){
matR[i][k] = 0;
for(int j=0;j<ncolA;j++){
matR[i][k] += matA[i][j]*matB[j][k];
}
printf("%d\n", matR[i][k]);
}
}
}
感想
深夜テンションでプログラムを作ったので、なんか気持ち悪いプログラムになったかも。数学的にいい感じのインデックス名の付け方とかなかったかな...