D言語で作るLU分解 part2 ~PA=LU編~

2011-11-19 20:51:36 Theme: ブログ

前回はA = LUとなる一般的なLU分解を行いました。
プログラムも一般的な簡単なものになったかと思います。

前回のA = LUとなるLU分解では、たとえばA11=0のときには正常に計算できません。
(L21はU11で割っている。U11=A11であるのでL21はA11で割っている。)

そこで前回の予告通り、不完全ピボット選択となるPA = LU分解を説明したいと思います。
仕組みは簡単で
A = LU となる行列A,L,Uが存在するとすると、
両辺に置換行列Pを左からかけて
PA = PLU
となります。
PA = A'として
A' = PLU
となります。
つまり、Aを行について適当に置換させた行列A'のLU分解の結果はもちろんPLUとなります。

ではプログラムではどうなるでしょうか?
下は実際の似非フローチャート的なものです。

U[i][i]を生成

if( U[i][i] == 0 ){
//i行とi以降の行を入れ替え
--i;
continue;
}

となります。
if文の中の行の入れ替えは行列AとPについて行うので、
A,Pがstruct Matrix!T であり、その行についての配列演算子が使えるなら
入れ替える行の番号をiとjとすると、
T[] temp = A[i];
A[i] = A[j];
A[j] = temp;
と書くだけです。
Pについても同様にかけます。

では前回のプログラムに上記を組み込んだものを載せておきます。
Matrix!(T)[] pludec(Matrix!T Mat){
 int row = Mat.row;
 int column = Mat.column;
 //LU分解を行う
 Matrix!T L = Matrix!T(row,column,0);
 Matrix!T U = Matrix!T(row,column,0);
 Matrix!T P = Matrix!T(row,column,0);
 Matrix!T A = Matrix!T(row,column,0);
 
 //Lの対角成分をすべて1に
 for(int i=0;i<row && i<column;i++)
  L[i][i] = 1;

 if(Mat[0][0] == 0){
  //置換を行う
  for(int i=1;i<row;i++){
   if(Mat[i][0] != 0){//入れ替え
    for(int j=0;j<column;j++){
     A[0][j] = Mat[i][j];
     A[i][j] = Mat[0][j];
    }
    P[0][i] = 1;
    P[i][0] = 1;
   }else{
    for(int j=0;j<column;j++)
     A[i][j] = Mat[i][j];
    P[i][i] = 1;
   }
  }
 }else{
  for(int i=0;i<row;i++){
   P[i][i] = 1;
   for(int j=0;j<column;j++)
    A[i][j] = Mat[i][j];
  }
 }
 
 //まずUの一列目の計算を行う
 for(int j=0;j<column;j++)
  U[0][j] = A[0][j];
 
 //1行目のLについて計算を行う
 for(int i=1;i<row;i++)
  L[i][0] = A[i][0] / U[0][0];
 
 //1列目(行目)以降のL,Uについて計算する
 for(int i=1;i<row;i++){
  //Uについて
  for(int j=i;j<column;j++){
   U[i][j] = A[i][j];
   //ここから引いていく
   for(int k=0;k<i;k++)
    U[i][j] -= L[i][k]*U[k][j];
  }
  
  //0なら入れ替え
  if(U[i][i] == 0){
   for(int l=i+1;l<row;l++)
    if(Mat[l][0] != 0){//入れ替え
     T[] temp;
     temp = A[i];
     A[i] = A[l];
     A[l] = temp;

     temp = P[i];
     P[i] = P[l];
     P[l] = temp;
    }
   --i;
   continue;
  }
  
  //Lについて
  for(int j=i+1;j<row;j++){
   L[j][i] = A[j][i];
   //ここから引いていく
   for(int k=0;k<i;k++)
    L[j][i] -= L[j][k]*U[k][i];
   //最後に割ってやる
   L[j][i] /= U[i][i];
  }
 }
}

ただし、この方法でもまだ不完全で、もし最終行でU[i][i]が0だった場合は正常に動作しません。
その場合はさかのぼってPA=LUで計算しなおしか、PAQ=LU分解をして、こちらで再計算かどちらかになると思います。
次回は上記のどちらかについて書きたいと思います。

Comments

[Publish Your Comment]

Add your comment

Let's send a present!

Amebaおすすめキーワード

    アメーバに会員登録して、ブログをつくろう! powered by Ameba (アメーバ)|ブログを中心とした登録無料サイト