この節のプログラムは,ピボット選択がないため,実用上問題を含んているが,最初の学 習としてこれは気にしないことにする.ガウス・ジョルダン法のプログラムの学習は簡単 な方が良い.皆さんが,学習ではなく実際に使うプログラムを組むときはピボット選択は 必要不可欠と考えなくてはならない.
さらに,このプログラムは行列が特異な場合でも,計算を続行しようする.その場合,ゼ ロで割ることが生じますので,実行時エラーが発生するであろう.
for(ipv=1 ; ipv <= n ; ipv++){
対角化の処理
}
ここで,ipvは対角化する要素
![]() |
数学では,対角成分を1にするために,その行を対角成分で割る.しかし,
コンピューターのプログラムでは予め逆数を計算して,それを乗じた方が良い.コン
ピューターは除算よりも乗算の方が得意なので効率が良いためである.非同次項
の演算は1回ですが,係数行列
は列毎なので
回の演算が必
要になる.対角成分を1にする処理は,次のようにする.
inv_pivot = 1.0/a[ipv][ipv];
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
![]() |
このように変形するのは簡単である.例えば,
行を処理する場合を考える.
行を,ピボットのある
行を
倍したもので引けば良いのである.
![]() |
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
これで対角化の処理はおしまい.
/* ========== ガウスジョルダン法の関数 =================*/
void gauss_jordan(int n, double a[][100], double b[]){
int ipv, i, j;
double inv_pivot, temp;
for(ipv=1 ; ipv <= n ; ipv++){
/* ---- 対角成分=1(ピボット行の処理) ---- */
inv_pivot = 1.0/a[ipv][ipv];
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
/* ---- ピボット列=0(ピボット行以外の処理) ---- */
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
}
}
ピボット選択,ここでは行の交換のみの部分選択を考える.その処理は,
![]() |
ここで,もし最大値がゼロの場合,行列は特異(行列式がゼロ)ということにな り,解は一意的に決まらない.その場合,関数の値としてゼロを返し,その ことをコールした側に伝えるのが良い.
big=0.0;
for(i=ipv ; i<=n ; i++){
if(fabs(a[i][ipv]) > big){
big = fabs(a[i][ipv]);
pivot_row = i;
}
}
if(big == 0.0){return(0);}
row[ipv] = pivot_row;
このプログラムは,以下のことを行っている.
![]() |
ただし,もともと最大の値が
行にある場合は,行の入れ替えは行わない.
if(ipv != pivot_row){
for(i=1 ; i<=n ; i++){
temp = a[ipv][i];
a[ipv][i] = a[pivot_row][i];
a[pivot_row][i] = temp;
}
temp = b[ipv];
b[ipv] = b[pivot_row];
b[pivot_row] = temp;
}
/* ========= ガウスジョルダン法の関数====================== */
int gauss_jordan(int n, double a[][MAXN+10], double b[]){
int ipv, i, j;
double inv_pivot, temp;
double big;
int pivot_row, row[MAXN+10];
for(ipv=1 ; ipv <= n ; ipv++){
/* ---- 最大値探索 ---------------------------- */
big=0.0;
for(i=ipv ; i<=n ; i++){
if(fabs(a[i][ipv]) > big){
big = fabs(a[i][ipv]);
pivot_row = i;
}
}
if(big == 0.0){return(0);}
row[ipv] = pivot_row;
/* ---- 行の入れ替え -------------------------- */
if(ipv != pivot_row){
for(i=1 ; i<=n ; i++){
temp = a[ipv][i];
a[ipv][i] = a[pivot_row][i];
a[pivot_row][i] = temp;
}
temp = b[ipv];
b[ipv] = b[pivot_row];
b[pivot_row] = temp;
}
/* ---- 対角成分=1(ピボット行の処理) ---------- */
inv_pivot = 1.0/a[ipv][ipv];
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
/* ---- ピボット列=0(ピボット行以外の処理) ---- */
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
}
return(1);
}
![]() |
これを実現するためには,以下の2つのことをすればよいだけである.
for(i=1 ; i<=n ; i++){
for(j=1 ; j<=n ; j++){
if(i == j){
inv_a[i][j]=1.0;
}else{
inv_a[i][j]=0.0;
}
}
}
temp = a[ipv][i]; a[ipv][i] = a[pivot_row][i]; a[pivot_row][i] = temp; temp = inv_a[ipv][i]; /* -- これ追加 -- */ inv_a[ipv][i] = inv_a[pivot_row][i]; /* -- これ追加 -- */ inv_a[pivot_row][i] = temp; /* -- これ追加 -- */と書き換えます.
a[ipv][j] *= inv_pivot; inv_a[ipv][j] *= inv_pivot; /* -- これ追加 -- */と書き換える.
a[i][j] -= temp*a[ipv][j]; inv_a[i][j] -= temp*inv_a[ipv][j]; /* -- これ追加 -- */と書き換える.
/* ========= ガウスジョルダン法の関数====================== */
int gauss_jordan(int n, double a[][MAXN+10], double b[],
double inv_a[][MAXN+10]){
int ipv, i, j;
double inv_pivot, temp;
double big;
int pivot_row, row[MAXN+10];
/* ---- 単位行列作成 ---------------------------- */
for(i=1 ; i<=n ; i++){
for(j=1 ; j<=n ; j++){
if(i==j){
inv_a[i][j]=1.0;
}else{
inv_a[i][j]=0.0;
}
}
}
for(ipv=1 ; ipv <= n ; ipv++){
/* ---- 最大値探索 ---------------------------- */
big=0.0;
for(i=ipv ; i<=n ; i++){
if(fabs(a[i][ipv]) > big){
big = fabs(a[i][ipv]);
pivot_row = i;
}
}
if(big == 0.0){return(0);}
row[ipv] = pivot_row;
/* ---- 行の入れ替え -------------------------- */
if(ipv != pivot_row){
for(i=1 ; i<=n ; i++){
temp = a[ipv][i];
a[ipv][i] = a[pivot_row][i];
a[pivot_row][i] = temp;
temp = inv_a[ipv][i];
inv_a[ipv][i] = inv_a[pivot_row][i];
inv_a[pivot_row][i] = temp;
}
temp = b[ipv];
b[ipv] = b[pivot_row];
b[pivot_row] = temp;
}
/* ---- 対角成分=1(ピボット行の処理) ---------- */
inv_pivot = 1.0/a[ipv][ipv];
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
inv_a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
/* ---- ピボット列=0(ピボット行以外の処理) ---- */
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
inv_a[i][j] -= temp*inv_a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
}
return(1);
}
メモリーと合わせて,計算効率も重要であった.大規模な計算になると,計算が 終了するまで何日も費やす場合がある.そのような場合,プログラムの改 良により,速度が10%アップするとかなりのメリットがあるのである.
そこで,ここではメモリーの効率的な利用を考える.ただし,ここは少し難しい.
![]() |
![]() |
これを実現するのは,簡単である.次のようにプログラムを書けばよい.
/* ---- 対角成分=1(ピボット行の処理) ---------- */
inv_pivot = 1.0/a[ipv][ipv];
a[ipv][ipv]=1.0; /* --- この行を追加 --- */
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
/* ---- ピボット列=0(ピボット行以外の処理) ---- */
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
a[i][ipv]=0.0; /* --- この行を追加 --- */
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
このようにすると必要なメモリーは,2.3.3節のプログラ
ムに比べて半分になる.さらに,計算時間も半分になる.
2.3.3節では,計算結果が0
や
の場合も計算し
ていたが,このプログラムではそれを省いている.
当然,
の逆行列ということは,乗算すると下のように単位行列になる.
![]() |
| (元の行列の |
このようなわけで,元の行列の行を入れ替えた場合,その逆行列は元の行列の逆行列の列 を入れ替えたものになる.従って,ピボット選択により係数行列の行を入れ替えると,逆 行列の列を入れ替える必要が生じる.実際にプログラムでは,以下のようにする.
/* ---- 列の入れ替え(逆行列) -------------------------- */
for(j=n ; j>=1 ; j--){
if(j != row[j]){
for(i=1 ; i<=n ; i++){
temp = a[i][j];
a[i][j]=a[i][row[j]];
a[i][row[j]]=temp;
}
}
}
これ以上,改良するのは大変なので,ほとんど問題なく使える関数のプログラ ムを以下に示す.
/* ========= ガウスジョルダン法の関数====================== */
int gauss_jordan(int n, double a[][MAXN+10], double b[]){
int ipv, i, j;
double inv_pivot, temp;
double big;
int pivot_row, row[MAXN+10];
for(ipv=1 ; ipv <= n ; ipv++){
/* ---- 最大値探索 ---------------------------- */
big=0.0;
for(i=ipv ; i<=n ; i++){
if(fabs(a[i][ipv]) > big){
big = fabs(a[i][ipv]);
pivot_row = i;
}
}
if(big == 0.0){return(0);}
row[ipv] = pivot_row;
/* ---- 行の入れ替え -------------------------- */
if(ipv != pivot_row){
for(i=1 ; i<=n ; i++){
temp = a[ipv][i];
a[ipv][i] = a[pivot_row][i];
a[pivot_row][i] = temp;
}
temp = b[ipv];
b[ipv] = b[pivot_row];
b[pivot_row] = temp;
}
/* ---- 対角成分=1(ピボット行の処理) ---------- */
inv_pivot = 1.0/a[ipv][ipv];
a[ipv][ipv]=1.0;
for(j=1 ; j <= n ; j++){
a[ipv][j] *= inv_pivot;
}
b[ipv] *= inv_pivot;
/* ---- ピボット列=0(ピボット行以外の処理) ---- */
for(i=1 ; i<=n ; i++){
if(i != ipv){
temp = a[i][ipv];
a[i][ipv]=0.0;
for(j=1 ; j<=n ; j++){
a[i][j] -= temp*a[ipv][j];
}
b[i] -= temp*b[ipv];
}
}
}
/* ---- 列の入れ替え(逆行列) -------------------------- */
for(j=n ; j>=1 ; j--){
if(j != row[j]){
for(i=1 ; i<=n ; i++){
temp = a[i][j];
a[i][j]=a[i][row[j]];
a[i][row[j]]=temp;
}
}
}
return(1);
}