ONLY DO WHAT ONLY YOU CAN DO

こけたら立ちなはれ 立ったら歩きなはれ

C++ で連立一次方程式を解く(LU分解法)

連立一次方程式
f:id:fornext1119:20140724235121p:plain

を行列で表すと, こんな感じ
f:id:fornext1119:20140724235224p:plain

このとき
f:id:fornext1119:20140724235907p:plain

f:id:fornext1119:20140725000028p:plain

f:id:fornext1119:20140725000214p:plain

とすると, 最初の連立方程式 Ax=b と表すことができる.

このとき  LU=A となる 上三角行列 U , 下三角行列 L を考えると,

 LUx=b であり,  Ux=y とおくと  Ly=b となる.

LU分解法では, 係数行列 A を上三角行列 U , 下三角行列 L に分解し,
 Ly=b から y を求め,  Ux=y から x を求める.

上三角行列 U は, ガウスの消去法で使った上三角行列そのもので,当然求め方も同じ.
下三角行列 L は, ガウスの消去法の計算途中で使った値を保存しておくだけ.
なので, ガウスの消去法とほとんど同じだが, 行列 b の値だけを変えて繰り返し実行する場合に使う.

#include <iostream>
#include <iomanip>
#include <math.h>
using namespace std;

void decomp(double a[4][4], double b[4], double x[4]);

int main()
{
    double a[4][4] = {{-1,-2,7,-2},{1,-1,-2,6},{9,2,1,1},{2,8,-2,1}}; 
    double b[4]    = {8,17,20,16};
    double x[4]    = {0,0,0,0};

    decomp(a,b,x);
    for (int i = 0;i<=3;i++)
        cout << setw(12) << fixed << setprecision(10) << x[i] << "\t";
    cout << endl;
   
    return 0;
}

void decomp(double a[4][4], double b[4], double x[4])
{
    // ピボット選択
    int p[4] = {0,1,2,3};
    for(int pivot = 0; pivot < 4; pivot++)
    {
        // 各列で 一番値が大きい行を 探す
        int     col     =   pivot;
        int     max_row =   pivot;
        double  max_val =   0;
        for (int i = p[pivot]; i < 4; i++)
        {
            int row = p[i];
            if (fabs(a[row][col]) > max_val)
            {
                // 一番値が大きい行
                max_val =   fabs(a[row][col]);
                max_row =   row;
            }
        }

        // 一番値が大きい行と入れ替え
        if (max_row != p[pivot])
        {
            int tmp     =   p[pivot];
            p[pivot]    =   max_row;
            p[max_row]  =   tmp;
        }
    }
    
    // 並べ替え後の状態を確認
    for (int i = 0; i < 4; i++) 
    {
        for (int j = 0; j < 4; j++) 
            cout << setw(14) << fixed << setprecision(10) << a[p[i]][j] << "\t";
        cout << setw(14) << fixed << setprecision(10) << b[p[i]] << "\t";
        cout << endl;
    }
    cout << endl;

    // 前進消去
    for (int pivot = 0; pivot < 3; pivot++)
    {
        int p_row = p[pivot];
        int p_col = pivot;
        
        for (int i = pivot+1; i < 4; i++)
        {
            int     row = p[i];
            double  s   = a[row][p_col] / a[p_row][p_col];
            
            for (int col = pivot; col < 4; col++)
                a[row][col] -= a[p_row][col] * s; // これが 上三角行列
            a[row][p_col] = s;                    // これが 下三角行列
            // b[row]    -= b[p_row] * s;         // この値は変更しない
        }
    }
    // 前進消去後の状態を確認
    for (int i = 0; i < 4; i++) 
    {
        for (int j = 0; j < 4; j++) 
            cout << setw(14) << fixed << setprecision(10) << a[p[i]][j] << "\t";
        cout << setw(14) << fixed << setprecision(10) << b[p[i]] << "\t";
        cout << endl;
    }
    cout << endl;
    
    // Ly=b から y を求める
    double y[4] = {0,0,0,0};
    for (int i = 0; i < 4; i++)
    {
        int row = p[i];
        for (int col = 0; col < i; col++)
            b[row] -= a[row][col] * y[p[col]];
        y[row] = b[row];
    }
    // y の 値
    for (int i = 0;i<=3;i++)
        cout << setw(12) << fixed << setprecision(10) << y[p[i]] << "\t";
    cout << endl << endl;

    // Ux=y から x を求める
    for (int i = 3; i >= 0; i--)
    {
        int row = p[i];
        for (int col = 3; col >= i + 1; col--)
            y[row] -= a[row][col] * x[col];
        x[i] = y[row] / a[row][i];
    }
}
  9.0000000000	  2.0000000000	  1.0000000000	  1.0000000000	 20.0000000000	
  2.0000000000	  8.0000000000	 -2.0000000000	  1.0000000000	 16.0000000000	
 -1.0000000000	 -2.0000000000	  7.0000000000	 -2.0000000000	  8.0000000000	
  1.0000000000	 -1.0000000000	 -2.0000000000	  6.0000000000	 17.0000000000	

  9.0000000000	  2.0000000000	  1.0000000000	  1.0000000000	 20.0000000000	
  0.2222222222	  7.5555555556	 -2.2222222222	  0.7777777778	 16.0000000000	
 -0.1111111111	 -0.2352941176	  6.5882352941	 -1.7058823529	  8.0000000000	
  0.1111111111	 -0.1617647059	 -0.3750000000	  5.3750000000	 17.0000000000	

20.0000000000	11.5555555556	12.9411764706	21.5000000000	

1.0000000000	2.0000000000	3.0000000000	4.0000000000	
参考文献