Processing math: 0%

2015年1月11日日曜日

N元連立1次方程式の解法プログラム組んでみた

せっかく数式が使えるようになったしちょっと数学系書いてみる
N元連立1次方程式にはいろいろ解き方あるけど、アルゴリズムが一番楽なクラメルの公式

クラメルの公式とは

Aを正方行列としたとき Ax=bで与えられる連立方程式のi番目の解は x_i=\frac{det(A_i)}{det(A)}

計算の流れ

公式だけでは説明不足で解らいので、例を使って解説

\left\{ \begin{array} _2x_1 + 4x_2 = 10 & \\ x_1 + 3x_2 = 6 & \end{array} \right. \tag{1} 式1をクラメルの式を使って解く
行列でAx=bの形に直すと
\left( \begin{array}{ccc} 2 & 4 \\ 1 & 3 \end{array} \right) \left( \begin{array}{ccc} x_1 \\ x_2 \end{array} \right) = \left( \begin{array}{ccc} 10 \\ 6 \end{array} \right) よって、 A = \left( \begin{array}{ccc} 2 & 4 \\ 1 & 3 \end{array} \right) 、 b = \left( \begin{array}{ccc} 10\\ 6 \end{array} \right)
  • クラメルの公式の分母
  • 分母は行列Aの行列式
    det(A) = 2\times3-4\times1=2
  • クラメルの公式の分子
  • x_iの場合の行列A_iは行列Ai番目の列とbを入れ替えた行列
    1. x_1の場合 :
    2. Aの1列目をbと入れ替える A_1 = \left( \begin{array}{ccc} 10 & 4 \\ 6 & 3 \end{array} \right) 計算すると det(A_1) = 10\times3-4\times6=6
    3. x_2の場合 :
    4. Aの2列目をbと入れ替える A_2 = \left( \begin{array}{ccc} 2 & 10 \\ 1 & 6 \end{array} \right) 計算すると det(A_2) = 2\times6-1\times10=2
  • 解を求める
  • \left\{ \begin{array} _x_1 = \frac{6}{2}=3 \\ x_2 = \frac{2}{2}=1 \end{array} \right.

プログラム

これをN次元でも解けるようにするプログラムを組む
上のアルゴリズムから、
  1. 行列式を求めるモジュール
  2. 行列の列を入れ替えるモジュール
を作ればいい
数値計算プログラムはCとかFortranで書いた方がいいんだけど、環境がなかったので。。javaを使用
できるだけ、プリミティブ型で書いて、別言語に書き換えやすくしとく
  • 行列式を求めるモジュール
  • 余因子展開使って再帰関数で作ってく。N×N行列の余因子展開の公式はコレ
    \begin{eqnarray} det(A)=\sum^n_{j=0}(-1)^j \cdot a_{0j} \cdot det(A_j) \end{eqnarray} プログラムにするとこんな感じ。
    1. /** 行列式の計算.
    2. * @param A 行列
    3. * @param n 行列の長さ
    4. * @return 計算結果
    5. */
    6. static double dat(double[][] matrix, int n) {
    7. if(n == 1) return matrix[0][0];
    8. if(n == 2) return matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1];
    9. double sum = 0;
    10. for(int j = 0; j < n; j++) {
    11. int sign = ((j % 2) == 0) ? 1 : -1;
    12. double[][] laplace_marix = createLaplaceMarix(matrix,n,0,j);
    13. sum += sign * matrix[j][0] * dat(laplace_marix, n-1);
    14. }
    15. return sum;
    16. }
    17. /** 余因子展開で使う行列を作る.
    18. * @param A 元の行列
    19. * @param n 元の行列の長さ
    20. * @param line 消す行
    21. * @param col 消す列
    22. * @return
    23. */
    24. static double[][] createLaplaceMarix(double[][] matrix, int n, int line, int col) {
    25. double laplace_marix[][] = new double[n-1][n-1]; // 余因子展開で使う行列
    26. for(int i = 0, y = 0; i < n; i++) {
    27. if(i == col)continue;
    28. for(int j = 0, x = 0; j < n; j++){
    29. if(j == line)continue;
    30. laplace_marix[y][x] = matrix[i][j];
    31. x++;
    32. }
    33. y++;
    34. }
    35. return laplace_marix;
    36. }
  • 行列の列を入れ替えるモジュール
  • まぁこれは簡単か。
    1. /** 行列の列を入れ替える.
    2. * @param matrix 元の行列
    3. * @param n 列の長さ
    4. * @param col1 入れ替える列
    5. * @param col2 入れ替わる列
    6. * @return 変換後の行列
    7. */
    8. static double[][] changeColumn(double[][] matrix, int n, int col1, int col2) {
    9. double[][] temp_matrix = new double[n][n+1];
    10. // 配列コピー
    11. for(int i = 0; i < n; i++){
    12. for(int j = 0; j < n + 1; j++) {
    13. temp_matrix[i][j] = matrix[i][j];
    14. }
    15. }
    16. // 列の入れ替え
    17. for(int i = 0; i < n; i++) {
    18. double temp = temp_matrix[i][col1];
    19. temp_matrix[i][col1] = temp_matrix[i][col2];
    20. temp_matrix[i][col2] = temp;
    21. }
    22. return temp_matrix;
    23. }
  • メイン文
  • モジュールができたので、組み立てる
    1. public static void main(String[] args) {
    2. int n = 2; // 連立方程式の元
    3. double matrix[][]={ // 連立方程式を行列化
    4. {2, 4, 10}, // 2x + 4y = 10 - 式1
    5. {1, 3, 6}, // x + 3y = 6 - 式2
    6. };
    7. double denominator = dat(matrix, n); // dat(A) 分母
    8. double[] result = new double[n]; // 答えの配列
    9. for(int i = 0; i < n; i++) {
    10. double[][] molecules_matrix = changeColumn(matrix, n, i, n);// 分子の行列
    11. result[i] = dat(molecules_matrix, n) / denominator;
    12. }
    13. // 解を表示
    14. for(int i = 0; i < n; i++) {
    15. System.out.println("result["+ i +"] : " + result[i]);
    16. }
    17. }

実行してみる

4元連立方程式で試してみる。 \left\{ \begin{array} _2x_1 + x_2+3x_3+4x_4 = 2 & \\ 3x_1 + 2x_2+5x_3+2x_4 = 12 & \\ 3x_1 + 4x_2+x_3-x_4 = 4 & \\ -x_1 + -3x_2+x_3+3x_4 = -1 & \\ \end{array} \right. \tag{2} 配列にすると、こう書けばOK
  1. double matrix[][]={ // 連立方程式を行列化
  2. // x1 x2 x3 x4 b ※bは移行してるわけではないので、右辺のbを書けばOK
  3. { 2.0, 1.0, 3.0, 4.0, 2.0}, //式1: 2x + y + 3z + 4w = 2
  4. { 3.0, 2.0, 5.0, 2.0, 12.0},//式2: 3x + 2y + 5z + 2w = 12
  5. { 3.0, 4.0, 1.0,-1.0, 4.0}, //式3: 3x + 4y + z - 1w = 4
  6. {-1.0,-3.0, 1.0, 3.0,-1.0} //式4:-1x - 3y + z + 3w = -1
  7. };
回答がこんな感じ
  1. result[0] : 1.0
  2. result[1] : -1.0
  3. result[2] : 3.0
  4. result[3] : -2.0
できたー!

0 件のコメント:

コメントを投稿