C言語で実装する行列の乗算と正規化|アルゴリズムとサンプルコード
C言語では、行列の乗算や正規化といった線形代数の基本操作を手軽に実装できます。本記事では、行列の乗算が成り立つための条件とアルゴリズム、さらに行列の正規化(ノーマライゼーション)の考え方を、実際に動作するC言語のサンプルコードと実行結果をもとにわかりやすく解説します。
行列の乗算とは
行列の乗算は、一定の条件を満たす場合にのみ定義されます。2つの行列 P と Q の次元がそれぞれ P(a × b)、Q(z × y)であるとしましょう。積の行列 R を求められるのは、「P の列数 b と Q の行数 z が一致する場合」だけです。つまり b = z が必要条件であり、そのとき結果の行列 R の大きさは(a × y)になります。
行列乗算のアルゴリズム
matrixMultiply(P, Q):
P の次元を (a × b)、Q の次元を (z × y) と仮定する
Begin
b と z が等しくなければ処理を終了
そうでなければ R を (a × y) の行列として定義
for i in range 0 to a - 1, do
for j in range 0 to y - 1, do
for k in range 0 to z - 1, do
R[i, j] = R[i, j] + (P[i, k] * Q[k, j])
done
done
done
End
三重のループを回しながら、P の i 行目の要素と Q の j 列目の要素を順に掛け合わせて総和を取ることで、R の (i, j) 成分が求まります。計算量は O(a × y × z) です。
行列の正規化とは
正規化とは、各行ベクトルの長さ(ノルム)を 1 に揃える処理のことです。具体的には、行内の各要素を「その行の要素の二乗和の平方根」で割ります。
例として、次のような 2 × 3 の行列を考えてみます。
4 5 6 1 2 3
この行列を正規化すると、各要素は次のように変換されます。
4/√(5² + 6²) 5/√(5² + 6²) 6/√(5² + 6²) 1/√(2² + 3²) 2/√(2² + 3²) 3/√(2² + 3²)
C言語による実装例
#include <stdio.h>
#include <math.h>
int main() {
int row, col, row1, col1;
int assignMatrix[50][50];
long long int a, square[50] = {0};
double norm[50][50], k;
printf("行列のサイズを入力してください\n");
scanf("%d %d", &row, &col);
printf("%d X %d の行列を入力してください\n", row, col);
for (row1 = 0; row1 < row; row1++) {
for (col1 = 0; col1 < col; col1++) {
scanf("%d", &assignMatrix[row1][col1]);
}
}
printf("\n行数: %d 列数: %d 入力された要素:\n", row, col);
for (row1 = 0; row1 < row; row1++) {
for (col1 = 0; col1 < col; col1++) {
printf("%d ", assignMatrix[row1][col1]);
}
printf("\n");
}
/* 各行の要素の二乗和を求める */
for (row1 = 0; row1 < row; row1++) {
for (col1 = 1; col1 < col; col1++) {
a = assignMatrix[row1][col1];
square[row1] += a * a;
}
printf("行 %d の二乗和: %lld\n", row1, square[row1]);
}
/* 正規化された行列を作成 */
for (row1 = 0; row1 < row; row1++) {
k = 1.0 / sqrt(square[row1]);
for (col1 = 0; col1 < col; col1++) {
norm[row1][col1] = k * assignMatrix[row1][col1];
}
}
printf("\n正規化された行列:\n");
for (row1 = 0; row1 < row; row1++) {
for (col1 = 0; col1 < col; col1++) {
printf("%.3lf ", norm[row1][col1]);
}
printf("\n");
}
return 0;
}
実行結果
行列のサイズを入力してください 2 3 2 X 3 の行列を入力してください 4 5 6 1 2 3 行数: 2 列数: 3 入力された要素: 4 5 6 1 2 3 行 0 の二乗和: 61 行 1 の二乗和: 13 正規化された行列: 0.512 0.640 0.768 0.277 0.555 0.832
解説のポイント
- 二乗和の計算: 1行目では 5² + 6² = 61、2行目では 2² + 3² = 13 となり、これが各行のノルム計算のもとになります。
- 正規化の工夫: 変数 k に「1 / √(二乗和)」をあらかじめ求めておき、各行の全要素に掛けることで除算の回数を減らしています。
- 表示形式: 書式指定子 %.3lf により小数第3位まで表示されます。たとえば 4 ÷ √61 ≒ 0.512 です。
行列の乗算と正規化は、機械学習のデータ前処理やコンピュータグラフィックス、数値計算など、幅広い分野で応用される基礎技術です。ぜひご自身の環境でもコードを動かして、挙動を確認してみてください。
-
Cプログラムで2次元行列の四隅の要素とその合計を出力する方法
概要 サイズ2×2以上の2次元配列(行列)が与えられたとき、その四隅に格納されているすべての要素を取り出し、合計を求めて出力するのが本記事のテーマです。 四隅の要素とは? 行数「r」、列数「c」(いずれも0から開始)をもつ行列 mat[r][c] を考えると、四隅の要素は次の4つになります。 左上:mat[0][0] 右上:mat[0][c-1] 左下:mat[r-1][0] 右下:mat[r-1][c-1] したがって、求める合計は次の式で表されます。mat[0][0] + mat[0][c-1] + mat[r-1][0] + mat[r-1][c-1]これらの四隅の要素を取得して加算
-
C++で対角行列・スカラー行列を判定するプログラムの書き方
行列 M[r][c] が与えられたとき、「r」は行数、「c」は列数を表し、r = c のとき正方行列となります。本記事では、与えられた正方行列が対角行列であるか、スカラー行列であるかを判定し、該当する場合には「yes」を出力する方法を解説します。 対角行列とは 正方行列 m[][] が対角行列であるのは、主対角線以外の要素がすべてゼロである場合、かつその場合に限ります。 下図のように、赤色で示された要素が主対角成分(非ゼロ)であり、それ以外の要素はすべてゼロになっているため、この行列は対角行列です。 入出力例 Input: m[3][3] = { {7, 0, 0}, {0, 8, 0}