【C言語】余因子展開を利用した行列式の求め方

C言語での余因子展開を利用した行列式の求め方の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、C言語での「余因子展開を利用した行列式の求め方」について解説していきます。

下記のページで行列式の求め方を解説しましたが、ここで解説したのは3次以下の行列式の求め方のみでした。

C言語での3次以下の行列式の求め方解説ページアイキャッチ 【C言語】3次以下の行列式の求め方

このページで紹介する余因子展開を利用すれば、理論的にはどんな次数の行列式であっても求めることができるようになります!もちろん4次の正方行列(4×4 の行列)の行列式も求められますし、10次の正方行列の行列式を求めることも可能です!

また、C言語での行列の扱い方については下記ページで解説していますので、そもそもC言語で行列を扱う方法をご存知ない方は、事前に下記ページを読んでいただくことをオススメします。

C言語での行列の扱い方の解説ページアイキャッチ 【C言語】行列の扱い方

今回は上記ページの ポインタで行列を扱う で紹介している関数を利用し、ポインタで行列を扱っていきます。

余因子展開を利用した行列式の求め方

まずはいつも通り、数学的観点から余因子展開を利用した行列式の求め方のおさらいをしていきたいと思います。

余因子

まず余因子についておさらいしていきたいと思います。

余因子 \(A_{ij}\) は、”正方行列 \(A\) の \(i\) 行目と \(j\) 列目を取り除いた正方行列” の行列式に \((-1)^{i + j}\) を掛けたものとなります。

例えば下記のような3次の正方行列 \(A\) においては、

$$ A = \left ( \begin{array}{ccc} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{array} \right ) $$

余因子 \(A_{23}\) は下記となります。

$$ A_{23} = (-1)^{2 + 3} \begin{vmatrix} a_{11} & a_{12} \\ a_{31} & a_{32} \end{vmatrix} $$

特に下記部分は、

$$ \begin{vmatrix} a_{11} & a_{12} \\ a_{31} & a_{32} \end{vmatrix} $$

下の図より、元々の正方行列 \(A\) から2行目と3列目を取り除いた行列の行列式となっていることが確認できると思います。

余因子を求める際に必要になる行列式

この余因子を利用すれば、行列式を簡単に求めることができます。

スポンサーリンク

余因子展開を利用した行列式の算出

さらに、”ある行” or “ある列” に対する全ての成分と余因子との積を足し合わせた結果は、元々の行列の行列式に等しいという特性があります。

行列式を余因子展開で求める様子

例えば、前述で示した3次の正方行列 \(A\) を2行目に対する全ての成分と余因子との積を足し合わせる計算は下記の式で表すことができます。

$$ a_{21} A_{21} + a_{22} A_{22} + a_{23} A_{23} $$

そして、上式の結果が、元々の正方行列 \(A\) の行列式となります。

$$ \begin{vmatrix} A \end{vmatrix} = a_{21} A_{21} + a_{22} A_{22} + a_{23} A_{23} $$

このように、行列式を “ある行” or “ある列” に対する全ての成分と余因子との積を足し合わせる式に展開することを余因子展開と言います。同じ行 or 同じ列に対して展開するのであれば、どの行 or どの列に対して展開しても同じ結果が得られます。

さらに、余因子 \(A_{ij}\) は、”正方行列 \(A\) の \(i\) 行目と \(j\) 列目を取り除いた” 正方行列の行列式に \((-1)^{i + j}\) を掛けたものですので、上式は次の式に変形することが可能です。

$$ \begin{vmatrix} A \end{vmatrix} = a_{21} (-1)^{2 + 1} \begin{vmatrix} a_{12} & a_{13} \\ a_{32} & a_{33} \end{vmatrix} + a_{22} (-1)^{2 + 2} \begin{vmatrix} a_{11} & a_{13} \\ a_{31} & a_{33} \end{vmatrix} + a_{23} (-1)^{2 + 3} \begin{vmatrix} a_{11} & a_{12} \\ a_{31} & a_{32} \end{vmatrix} $$

ここで思い出して欲しいのが、元々の正方行列 \(A\) が3次の正方行列であったという点です。

それに対し、上記の余因子展開を利用した計算式の右辺に注目すれば分かる通り、右辺で求める必要がある行列式は2次の正方行列のもののみです。すなわち、2次の行列式のみから3次の行列式を求めることができています。

ここが行列式算出次に余因子展開を利用する一番のポイントで、余因子展開を利用した場合、次数を1つ落とした行列式から行列式を算出することが可能です。

4次の行列式の算出

ですので、余因子展開を利用すれば、4次の行列式は「3次の行列式」さえ解ければ算出可能ということになります。

例えば下記のような4次の正方行列 \(A\) であれば、

$$ A = \left ( \begin{array}{cccc} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \\ a_{41} & a_{42} & a_{43} & a_{44} \end{array} \right ) $$

1 列目に対して余因子展開すれば、下記の式で行列式を求めることができます。

$$ \begin{vmatrix} A \end{vmatrix} = a_{11} (-1)^{1 + 1} \begin{vmatrix} a_{22} & a_{23} & a_{24} \\ a_{32} & a_{33} & a_{34} \\ a_{42} & a_{43} & a_{44} \end{vmatrix} + a_{21} (-1)^{2 + 1} \begin{vmatrix} a_{12} & a_{13} & a_{14} \\ a_{32} & a_{33} & a_{34} \\ a_{42} & a_{43} & a_{44} \end{vmatrix} \\ + a_{31} (-1)^{3 + 1} \begin{vmatrix} a_{12} & a_{13} & a_{14} \\ a_{22} & a_{23} & a_{24} \\ a_{42} & a_{43} & a_{44} \end{vmatrix} + a_{41} (-1)^{4 + 1} \begin{vmatrix} a_{12} & a_{13} & a_{14} \\ a_{22} & a_{23} & a_{24} \\ a_{32} & a_{33} & a_{34} \end{vmatrix} $$

あとは右辺の各項に現れる3次の行列式を解き、その結果を利用して上記の右辺を解けば良いだけです。

4次の行列式を3次の行列式を解いて求める様子

3次の行列式の解き方については下記ページで解説していますので、それを利用すればすぐに解くことができるはずです。

C言語での3次以下の行列式の求め方解説ページアイキャッチ 【C言語】3次以下の行列式の求め方

え?3次の行列式の計算式がややこしくて使いたくない?

であれば、各項の3次の行列式それぞれに対してさらに余因子展開を利用して次数を落としてやれば良いです。

余因子展開で現れた行列式をさらにもう一段階余因子展開する様子

これで2次の行列式の計算を行えば良いだけですね!

え?2次の行列式の計算式を記述するのもめんどくさい?

であれば、先ほどの余因子展開で現れた2次の行列式それぞれに対してさらに余因子展開を利用して次数を落としてやれば良いです。

余因子展開で現れた行列式をさらにもう一段階余因子展開する様子2

これで計算する必要があるのは1次の行列式だけであり、1次の場合は行列の成分そのものが行列式となるのでもう自明ですね!

こんな感じで、一度の余因子展開では次数が1下がるのみですが、それを段階的に繰り返すことで次数をさらに下げることができます。

そして、あなたが行列式を計算できる次数まで下がった時に、行列式を計算してやれば良いだけです。

N次の行列式の算出

ですので、5次以上の行列式についても同様で、余因子展開を段階的に繰り返していけば、計算する必要のある行列式の次数を3以下まで下げることができます。

あとは、その行列式を計算式に当てはめて解いてやれば行列式を解くことができます。

なので、余因子展開が扱えれば、理論上はどんな次数の行列式も求めることができるようになったことになります!ただ、あくまでも理論上の話で、次数が大きいほど計算量が多くなるので、次数が大きいと時間がかかりすぎて行列式を解くことはできません。

それでも10次程度の行列式であれば1秒くらいで解くことはできると思います(もちろんプログラムを実行する PC のスペックにもよります)。

また、余因子展開すればするほど式はどんどん複雑になっていくことになりますが、プログラムの場合は再帰呼び出しを利用すればすんなり解くことができるので安心してください!

スポンサーリンク

余因子展開を利用した行列式の算出の実装方法

続いて「余因子展開を利用した行列式の算出を行うプログラム」のC言語での実装について解説していきたいと思います。

前述の通り、再帰呼び出しを利用して行列式を算出していきたいと思います。

行列式を求める関数の準備

まず最初に、行列式を求める関数として下記を用意します。

行列式を求める関数
int det(int **mat, int n) {

}

まだ空関数ですが、以降の解説の中で、この det 関数を「n 次の正方行列 mat の行列式を返却する関数」として作成していきたいと思います。

n1 以上の自然数を想定しており、mat は下記ページの ポインタで行列を扱う で紹介している createMatrix 関数により作成される行列であることを想定した関数となっています(createMatrix 関数で作成しなくても、n 次の正方行列を指す int ** 型のポインタであれば引数 mat に指定可能です)。

C言語での行列の扱い方の解説ページアイキャッチ 【C言語】行列の扱い方

1次の行列式を求める

まずは det 関数で1次の行列式を求められるようにしていきたいと思います。

1次の行列式の求め方は下記ページでも解説していますが、

C言語での3次以下の行列式の求め方解説ページアイキャッチ 【C言語】3次以下の行列式の求め方

1次の正方行列の成分は1つのみであり、その成分そのものが行列式となります。

したがって、det 関数を下記のように変更すれば、det 関数で1次の正方行列 mat の行列式を求めることができるようになります。

1次の行列式を求める
int det(int **mat, int n) {
    if (n == 1) {
        /* 1次の行列式を算出して返却 */
        return mat[0][0];
    }
}

スポンサーリンク

N 次の行列式を求める

ここからがいよいよ本番で、余因子展開を利用して N 次の正方行列の行列式を解けるように det 関数を変更していきます。

余因子を求める

まずは、1つの余因子を求めることを考えていきたいと思います。

余因子展開を利用した行列式の求め方 で解説した通り、余因子 \(A_{ij}\) は「正方行列 \(A\) の \(i\) 行目と \(j\) 列目を取り除いた正方行列の行列式に \((-1)^{i + j}\) を掛けたもの」となります。

ここで「正方行列 \(A\) の \(i\) 行目と \(j\) 列目を取り除いた正方行列」を作成することを考えたいと思います。

この正方行列を s_mat とすれば、det 関数では引数 mat で正方行列 \(A\) が与えられるため、次のような処理により、上記の正方行列 s_mat を作成することができます。

小行列の作成
/* i行とj列の成分を除いた行列を作成 */
num_r = 0;
for (r = 0; r < n; r++) {
    if (r == i) {
        /* i行目の成分は行列に格納しない */
        continue;
    }

    num_c = 0;
    for (c = 0; c < n; c++) {
        if (c == j) {
            /* j行目の成分は行列に格納しない */
            continue;
        }

        /* 行列にi行とj列の成分以外を格納 */
        s_mat[num_r][num_c] = mat[r][c];

        /* 行列に格納した個数(列方向)をカウントアップ */
        num_c++;
    }
    /* 行列に格納した個数(行方向)をカウントアップ */
    num_r++;
}

上記のループ処理の中では、行を表す添字 ri 以外の場合と列を表す添字 cj 以外の場合のみ mat の成分を s_mat に格納する処理を行なっています。そのため、上記の処理が完了した際には、s_mat は正方行列 mati 行目とj 列目を取り除いた正方行列となります。

この正方行列 s_mat は、行・列とも mat よりも 1 少ないので、n - 1 次の正方行列となります。

さらに、余因子とは、この正方行列の行列式に \((-1)^{i + j}\) を掛けたものとなります。

まず \((-1)^{i + j}\) とは、結局は \(i + j\) が偶数の時に \(1\) になり、奇数の時に \(-1\) になる計算式です。

また、現状 n - 1 次の行列式を解く関数が存在しないですが、その関数名を仮で n-1次行列式 としておくことにすれば、余因子 \(A_{ij}\) は下記により求めることができることになります。

余因子を求める
/* 符号を算出 */
if ((i + j) % 2 == 0) {
    sign = 1;
} else {
    sign = -1;
}

/* (i,j) に対する余因子を求める */
cofactor_i_j = sign * n-1次行列式(s_mat);

この cofactor_i_j が、行列 mat の (i, j) に対する余因子となります。

n-1次行列式 の関数が用意できていないので現状ではコンパイルが通りませんので、n-1次行列式 の部分は後から差し替えて対応します。

MEMO

数学で扱う行列の成分の添字が \(1\) から始まるのに対し、C言語の配列で扱う添字は 0 から始まります

そのことを考慮すれば、\((-1)^{i + j}\) と同等の処理を行うための判断式である ((i + j) % 2 == 0)((i + 1 + j + 1) % 2 == 0) と書いた方が正確かもしれませんが、いずれにせよ必ず結果は必ず一緒になるため、上記では ((i + j) % 2 == 0) により判断を行うようにしています

余因子展開を利用して行列式を求める

余因子を求められるようになったので、次は余因子展開を利用して n 次の正方行列 mat の行列式を求めるようにしていきたいと思います。

余因子展開を利用した行列式の求め方 で解説した通り、余因子展開とは “ある行” or “ある列” に対する全ての成分と余因子との積を足し合わせることであり、この結果が行列式の結果と一致します。

行列式を余因子展開で求める様子

今回は、この余因子展開を 0 列目に対して行うようにしたいと思います。

この場合、余因子展開を利用した行列式の算出は、列を表す添字 j0 に固定した状態で i を 0 から n - 1 まで変化させながら 余因子を求める で紹介した処理により各因子 cofactor_i_j を求め、その求めた cofactor_i_j と行列 mat の成分 mat[i][j] を足し合わせていくことにより実現することができます。

具体的には下記の処理により、n 次の正方行列 mat の行列式 det を求めることができます。

余因子展開を利用した行列式の算出
/* 展開する列を設定 */
j = 0;

/* n次の行列式を一旦0にセット */
det_n = 0;

/* j列に対して余因子展開 */
for (i = 0; i < n; i++) {

    /* cofactor_i_jを求める */

    /* (i,j)に対する余因子と(i,j)成分を掛けたものを足し合わせる */
    det_n += mat[i][j] * cofactor_i_j;

}

上記のループ処理完了後の det_ndet 関数で返却すべき行列式となります。

余因子展開を利用して行列式を求める関数

ここまでの解説を全て踏まえ、さらに変数宣言や行列の作成等も行うようにすれば、det 関数は下記のようになります。

det関数
int det(int **mat, int n) {

    int **s_mat = NULL;
    int i, j; /* 余因子を求める行と列 */
    int r, c; /* 行と列 */
    int num_r, num_c; /* 行列に成分を格納した個数(行と列) */
    int det_n; /* n次の行列式の算出結果 */
    int cofactor_i_j; /* (i,j)余因子 */
    int sign; /* 余因子展開した項の符号 */

    if (n == 1) {
        /* 1次の行列式を算出して返却 */
        return mat[0][0];
    }

    /* n-1次の正方行列を作成 */
    s_mat = createMatrix(n - 1, n - 1);
    if (s_mat == NULL) {
        printf("malloc error\n");
        return 0;
    }

    /* 展開する列を設定 */
    j = 0;

    /* n次の行列式を一旦0にセット */
    det_n = 0;

    /* j列に対して余因子展開 */
    for (i = 0; i < n; i++) {

        /* i行とj列の成分を除いた行列を作成 */
        num_r = 0;
        for (r = 0; r < n; r++) {
            if (r == i) {
                /* i行目の成分は行列に格納しない */
                continue;
            }

            num_c = 0;
            for (c = 0; c < n; c++) {
                if (c == j) {
                    /* j行目の成分は行列に格納しない */
                    continue;
                }

                /* 行列にi行とj列の成分以外を格納 */
                s_mat[num_r][num_c] = mat[r][c];

                /* 行列に格納した個数(列方向)をカウントアップ */
                num_c++;
            }
            /* 行列に格納した個数(行方向)をカウントアップ */
            num_r++;
        }

        /* 符号を算出 */
        if ((i + j) % 2 == 0) {
            sign = 1;
        } else {
            sign = -1;
        }

        /* (i,j)に対する余因子を求める */
        cofactor_i_j = sign * n-1次の行列式(s_mat);

        /* (i,j)に対する余因子と(i,j)成分を掛けたものを足し合わせる */
        det_n += mat[i][j] * cofactor_i_j;

    }

    /* 行列を削除 */
    deleteMatrix(s_mat, n - 1);

    /* n次の行列式を返却 */
    return det_n;

}

行列をポインタで扱うため、s_mat は2次元配列ではなくポインタのポインタとして変数宣言しています。

そのため、下記ページで紹介している createMatrix 関数を実行することで行列の作成(行列用のメモリの確保)を行なっています。

C言語での行列の扱い方の解説ページアイキャッチ 【C言語】行列の扱い方

createMatrix 関数の中では malloc 関数を利用してメモリを確保しているため、行列を使い終わった際にはメモリを解放する必要があります。

これを行なっているのが、return 直前に実行している deleteMatrix 関数となります。

この関数についても上記ページで紹介しています。

わざわざポインタで扱う理由は、次に説明する再帰呼び出しが関係しています。

det 関数の再起呼び出しを行う

ただし、先ほど紹介した det 関数は不完全で、n - 1 次の行列式を求める関数の呼び出しが行われていません。

n-1次の行列式の算出部分
/* (i,j)に対する余因子を求める */
cofactor_i_j = sign * n-1次の行列式(s_mat);

この n - 1 次の行列式を求める関数は、上記を次のように書き換えることで完成します。

det関数の再帰呼び出し
/* (i,j)に対する余因子を求める */
cofactor_i_j = sign * det(s_mat, n - 1);

すなわち、det 関数の中から第2引数に n - 1 を指定して det 関数自身を実行することで、n - 1 次の行列式を求めます。このように、関数の中から関数自身を呼び出すことを再帰呼び出しと言います。

そして、上記のように変更することで det 関数も完成したことになり、これにより N 次の行列式を求めることが可能になりました!

ただし、N が大きいと時間がかかりすぎて関数が終わらなくなったりメモリが足りなくなって行列式が上手く求められない場合があったりするので注意してください。この方法だと、現在の PC のスペックだと N = 12 くらいが限界かなぁという印象です。

また、行列式の算出先となる変数の型が int なので、N が大きいとオーバーフローが発生するので注意してください。N5 程度であっても行列の成分の値が大きいとオーバーフローが発生して上手く行列式が求められません。必要に応じて変数の型を longlong long 等に変更してやる必要があります。

det 関数で N 次の行列式が求められる理由

実装の仕方の最後の解説として、上記の det 関数で N 次の行列式が求められる理由を簡単に解説しておきます。

まず、det 関数で 1 次の行列式が求められることは納得していただけると思います。det 関数では下記の処理を行なっているので、1 次の行列式は下記部分で求めることができます。

1次の行列式の算出
if (n == 1) {
    /* 1次の行列式を算出して返却 */
    return mat[0][0];
}

また、2 次の行列式について考えると、余因子展開を利用した行列式の求め方 で解説した通り、余因子展開を利用すれば行列式は1次下げた状態の行列式から求めることができるため、この 2 次の行列式を求めるためには 1 次の行列式さえ求めれば良いことになります。

さらに、2 次の行列式を求める際には引数 n2 になっていますので、下記では det 関数で 1 次の行列式を求めることになります。

det関数で1次の行列式を求める
cofactor_i_j = sign * det(s_mat, n - 1);

前述の通り、det 関数では 1 次の行列式を求めることが可能ですので、結果的に det 関数は 2 次の行列式も求めることも可能ということになります。

3 次の行列式の場合も同様ですね!

3 次の行列式は余因子展開を利用すれば 2 次の行列式から求めることができます。

n = 3 の状態で下記のように det 関数を実行すれば 2 次の行列式を求めることになりますが、前述の通り、det 関数は 2 次の行列式を求めることが可能です。

det関数で2次の行列式を求める
cofactor_i_j = sign * det(s_mat, n - 1);

したがって、det 関数は 3 次の行列式も求めることも可能ということになります。

あとは同様で、N 次の行列式は余因子展開を利用すれば N - 1 次の行列式から求めることができ、さらに n = N の状態で下記のように det 関数を実行すれば N - 1 次の行列式が求められますので、結果的に det 関数は N 次の行列式も求めることが可能ということになります。

det関数でN-1次の行列式を求める
cofactor_i_j = sign * det(s_mat, n - 1);

おそらく 余因子展開を利用した行列式の求め方 の解説を読んだ時の印象よりも、det 関数がシンプルだったと思った方も多いのではないかと思います。

ポイントは再帰呼び出しで、この再帰呼び出しを利用すれば「難しい問題」を簡単に解けることが多いです。

下記ページで迷路を例に再帰呼び出しの解説をしていますので、興味がある方は下記ページを読んでみていただければと思います。迷路も再帰呼び出しを利用すれば簡単に解けます!

迷路を解いて再帰呼び出しを理解するページのアイキャッチ 【C言語】「再帰呼び出しの動き・メリット・書き方」を迷路を解いて理解する

ただ、1点注意があって、再帰呼び出しは難しい問題を簡単に解く素晴らしい手法ではあるのですが、スタックオーバーフローが発生しやすいので注意してください。発生するとプログラムが異常終了する可能性が高いです。

特に、再帰呼び出しを行う関数内で大きな配列などを変数宣言すると、よりスタックオーバーフローが発生しやすくなります。

その対策の意味も含めて、行列は det 関数内で配列として宣言するのではなく、createMatrix 関数の中で malloc 関数を利用して行列に必要なメモリを確保し、行列はポインタで扱うようにしています。

それでも PC にメモリが足りない場合や求める行列の次数が大きいとメモリ不足になって malloc 関数でエラーが発生してしまう可能性はあるので注意してください。

この場合は、下記のエラーメッセージが表示されます。このメッセージが出た場合は表示される行列式がデタラメな値になるので注意してください(メモリが足りないと計算できない)。

malloc error

余因子展開を利用した行列式の算出プログラムの例

最後に、先ほど解説した det 関数を呼び出す部分も含め、余因子展開を利用した行列式の算出プログラムのソースコード全体を載せておきます。

余因子展開を利用した行列式の算出
#include <stdio.h&g;t
#include <stdlib.h>

#define N 4 /* 正方行列の次数 */

/**************************************
 * m行n列の行列用のメモリを確保(成分の型はint)
 * m:行列の行数
 * n:行列の列数
 * 返却値:確保したメモリの先頭アドレス(失敗時はNULL)
 **************************************/
int **createMatrix(unsigned int m, unsigned int n) {

    int **mat;
    unsigned int i, j;

    if (n == 0 || m == 0) {
        printf("n or m is 0\n");
        return NULL;
    }

    /* m行分のポインタ格納用のメモリを確保 */
    mat = malloc(sizeof(int *) * m);
    if (mat == NULL) {
        return NULL;
    }

    for (i = 0; i < m; i++) {
        /* 1行分ずつメモリを確保する */

        /* n列分のint型のデータが格納できるメモリを確保 */
        mat[i] = malloc(sizeof(int) * n);
        if (mat[i] == NULL) {
            printf("malloc error\n");
            for (j = 0; j < i; j++) {
                free(mat[i]);
            }
            free(mat);
            return NULL;
        }
    }

    return mat;
}

/**************************************
 * 行列用のメモリを解放
 * m:行列の行数
 * 返却値:なし
 **************************************/
void deleteMatrix(int **mat, unsigned int m) {
    unsigned int i;

    for (i = 0; i < m; i++) {
        free(mat[i]);
    }

    free(mat);
}

int det(int **mat, int n) {

    int **s_mat = NULL;
    int i, j; /* 余因子を求める行と列 */
    int r, c; /* 行と列 */
    int num_r, num_c; /* 行列に成分を格納した個数(行と列) */
    int det_n; /* n次の行列式の算出結果 */
    int cofactor_i_j; /* (i,j)余因子 */
    int sign; /* 余因子展開した項の符号 */

    if (n == 1) {
        /* 1次の行列式を算出して返却 */
        return mat[0][0];
    }

    /* n-1次の正方行列を作成 */
    s_mat = createMatrix(n - 1, n - 1);
    if (s_mat == NULL) {
        printf("malloc error\n");
        return 0;
    }

    /* 展開する列を設定 */
    j = 0;

    /* n次の行列式を一旦0にセット */
    det_n = 0;

    /* j列に対して余因子展開 */
    for (i = 0; i < n; i++) {

        /* i行とj列の成分を除いた行列を作成 */
        num_r = 0;
        for (r = 0; r < n; r++) {
            if (r == i) {
                /* i行目の成分は行列に格納しない */
                continue;
            }

            num_c = 0;
            for (c = 0; c < n; c++) {
                if (c == j) {
                    /* j行目の成分は行列に格納しない */
                    continue;
                }

                /* 行列にi行とj列の成分以外を格納 */
                s_mat[num_r][num_c] = mat[r][c];

                /* 行列に格納した個数(列方向)をカウントアップ */
                num_c++;
            }
            /* 行列に格納した個数(行方向)をカウントアップ */
            num_r++;
        }

        /* 符号を算出 */
        if ((i + j) % 2 == 0) {
            sign = 1;
        } else {
            sign = -1;
        }

        /* (i,j)に対する余因子を求める */
        cofactor_i_j = sign * det(s_mat, n - 1);

        /* (i,j)に対する余因子と(i,j)成分を掛けたものを足し合わせる */
        det_n += mat[i][j] * cofactor_i_j;

    }

    /* 行列を削除 */
    deleteMatrix(s_mat, n - 1);

    /* n次の行列式を返却 */
    return det_n;

}

int main(void) {

    int **mat;

    /* N次の正方行列を作成 */
    mat = createMatrix(N, N);
    if (mat == NULL) {
        return 0;
    }

    /* 行列の各成分を格納 */
    mat[0][0] = 2; mat[0][1] = 1; mat[0][2] = 5; mat[0][3] = 3;
    mat[1][0] = 3; mat[1][1] = 0; mat[1][2] = 1; mat[1][3] = 6;
    mat[2][0] = 1; mat[2][1] = 4; mat[2][2] = 3; mat[2][3] = 3;
    mat[3][0] = 8; mat[3][1] = 2; mat[3][2] = 0; mat[3][3] = 1;

    /* 行列式を求めて表示 */
    printf("%d\n", det(mat, N));

    /* 不要になった行列を削除 */
    deleteMatrix(mat, N);

    return 0;
}

実行すれば、下記のように det 関数で求められた行列式が表示されます。

-742

また、main 関数で mat に格納する成分を変更してやれば他の行列の行列式を求めることもできますし、ソースコード先頭部分の N の定義値を変更すれば他の次数の行列式も求めることができます。

ただし、前述の通り、オーバーフローが発生する可能性もありますし、N が大きいと処理時間がかかりすぎて関数が終了しなくなる可能性もあるので注意してください。

まとめ

このページでは、C言語での「余因子展開を利用した行列式の求め方」について解説しました!

余因子展開を利用すれば、行列式を次数を1つ下げた行列式の解より求めることができます。ですので、4次の行列式は3次の行列式の解から求めることができ、3次の行列式さえ解くことができれば、4次の行列式も解くことができることになります。

さらに余因子展開を段階的に繰り返すことで、解く必要のある行列式の次数をどんどん下げていくことができ、大きな次数の行列式も3次の行列式 or 2次の行列式 or 1次の行列式を解くことで求めることができるようになります!

余因子展開を利用した行列式の算出は、手で計算するとめちゃめちゃ大変ですが、プログラムは割と簡単に作成することができます。

で、このプログラムを作成する時にポイントになるのが再帰呼び出しです。

余因子展開を利用した行列式の算出は、再帰呼び出しの動作を理解したり便利さを実感したりするのにも良い題材だと思いますので、このページで解説した内容はぜひ理解していってください!

ただ、今回紹介した余因子展開を利用した行列式の算出は処理速度的には遅く、高速に行列式を求めたい場合は行列の基本変形を利用して行列式を算出するのが良いと思います。

「行列の基本変形を利用した行列式の求め方」については下記ページで解説していますので、興味のある方はぜひこちらのページも読んでみていただければと思います!

C言語での行列の基本変形を利用した行列式の求め方の解説ページアイキャッチ 【C言語】行列の基本変形を利用した行列式の求め方

同じカテゴリのページ一覧を表示