08数値計算法
2008鳥取大学工学部知能情報工学科
田中美栄子
May 21, 2008
ガウスの消去法
• 連立一次方程式を解く
①式 a11 x1 a12 x2 a13 x3 ... a1n xn b1
②式 a21 x1 a22 x2 a23 x3 ... a2 n xn b2
...
an1 x1 an 2 x2 an 3 x3 ... ann xn bn
a11
①式 新①式 x1 a12 ' x 2 a13' x 3 ... a1n ' x n b1'
②式ー①式×a12
②式 新②式 0 a 22' x 2 a 23' x 3 ... a 2n ' x n b2 '
③式 新③式 0 a 32 ' x 2 a 33' x 3 ... a 3n ' x n b3 '
ガウスの消去法
• 次は右下の(n-1)×(n)行列に注目
①式 a11 x1 a12 x2 a13 x3 ... a1n xn b1
②式 a21 x1 a22 x2 a23 x3 ... a2 n xn b2
...
an1 x1 an 2 x2 an 3 x3 ... ann xn bn
x 2 a 23' ' x 3 ... a 2n " x n b1"
②式ー①式×a12
a 23" x 3 ... a 2n " x n b2 "
ガウスの消去法(4)
• 更新後、元と等価な次の連立方程式ができる
• ②式以下に注目すると(n-1)次の連立方程式
• この最初の式②をその第一係数で割り、次の式の第一係数
を掛けて次の式から引く、という操作を繰り返すと元と等価な
右上3角連立方程式ができる
①式 x1 a12 x2 a13 x3 ... a1n xn b1
②式 x2 a23 x3 ... a2 n xn b2
a33 x3 ... a3 n xn b3
...
n式 • これを下から順に解いてゆく
xn bn
ガウスの消去法(5)
最初から整理。係数だけ書くと次の操作となる。
a 11
a 21
a 12
a 22
a 13 . a 1n
a 23 . a 2 n
b1
b2
1 a 12
0 a 22
a 13
a 23
. a 1n
. a 2n
b1
b2
a 31
.
a 32
.
a 33 . a 3n
. . .
b3
.
0 a 32
0 .
a 33
.
. a 3n
. .
b3
.
a n1 a n 2
a n 3 . a nn
bn
0 a n2
a n 3 . a nn
bn
第k回目の操作は第k行目を a kk で割って、j=k,..,n
列目
( k 1)
: a kj
( k 1)
: b k
a kj
bk
(k)
(k)
/ a kk
/ a kk
(k)
(k)
ガウスの消去法(6)
• K+1行目以下は、 i=k+1,…,n行目について、
j=k,k+1,…,n列をそれぞれ a i ,k 倍して引くと
a
( k 1)
ij
: a
( k 1)
: bi
bi
(k)
ij
(k)
a ik a kj
(k)
a ik b k
(k)
( k 1)
( k 1)
ガウスの消去法(7)
更新k-1回目で係数は
( k 1)
( k 1)
( k 1)
( k 1)
( k 1)
a k ,k
a k ,k 1
a k ,k 2
. a k ,n
bk
( k 1)
( k 1)
( k 1)
( k 1)
( k 1)
a k 1,k
a k 1,k 1
a k 1,k 2
. a k 1,n
b k 1
a k 2,k
( k 1)
a k 2,k 1
.
a n ,k
1
0
( k 1)
a k 2,k 2
.
( k 1)
( k 1)
(k)
. a k 2,n
.
a n ,k 1
a k ,k 1
1
( k 1)
a n ,k 2
(k)
.
( k 1)
a k ,k 2
(k)
a k 1,k 2
bk2
.
. a n ,n
.
.
( k 1)
.
( k 1)
(k)
a k ,n
(k)
a k 1,n
0
0
1
. a k 2,n
.
.
.
.
.
0
0
0
.
a n ,n
( k 1)
(k)
bn
( k 1)
(k)
bk
(k)
b k 1
bk2
(k)
.
(k)
bn
(k)
ガウスの消去法(8)
• 後退代入で解を順に求める
x n bn
(n)
/ a n ,n
(n)
• あとはk=n-1,n-2,…,1の順に
x k bk
(n)
n
a
j k 1
(n)
k, j
xj
•
/** Gauss elimination :written by Mieko Tanaka May31, 2006 */
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
#include <stdio.h>
//#define N 4
//#define N 2
#define N 8
void main(){
double a[N][N+1]={{4.0, 2.0, 1.0, 0.0, 4.0,0.0, 2.0,0.0,3.0},
{1.0,6.0,0.0, 3.0, -1.0, 0.0, -3.0, 2.0, 0.0},
{1.0, -1.0, 2.0, -3.0, 4.0, 5.0, 0.0, 0.0, 3.0},
{2.0, 6.0, 0.0, 3.0,-1.0, 4.0, 0.0, 2.0,3.0},
{1.0, -1.0, 4.0, -3.0, 4.0, 5.0, 3.0, 0.0, 3.0},
{3.0, 6.0, 0.0, 3.0,-1.0, 4.0, 0.0, 2.0,3.0},
{-1.0, -1.0, -4.0, -3.0, 1.0, -2.0, 3.0, 0.0, 3.0},
{1.0, -1.0, -2.0, -3.0, 4.0, 1.0, 0.0, 7.0, 5.0},
};
// double a[N][N+1]={{1.0, 2.0, 3.0},{4.0,1.0,6.0}};
/*
double a[N][N+1]={{1.0,1.0,-3.0,-4.0,-1.0},
{2.0,1.0,5.0,1.0,5.0},
{3.0,6.0,-2.0,1.0,8.0},
{2.0,2.0,2.0,-3.0,2.0}};
*/
int i,j,k, count=0;
double x[N];
double p,q;
//pivot: arrange rows of matrix a to make diagonal nonzero
//forward elimination(always consider small matrix beginning a[k][k]
for(k=0;k<N;k++){
//for all k=0,1…,N-1
p=a[k][k];
//p memorizes a[k][k]
a[k][k]=1;
//Diagonal elements are set to 1
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
for(j=k+1;j<N+1;j++) {a[k][j]/=p;count++;} //Divide right elements (j=k+1,…,N)by p
for(i=k+1;i<N;i++) {
//for every i-th row (i=k+1,…
q=a[i][k]; //q memorizes leftmost element(k-th column)
for(j=k+1;j<N+1;j++) {a[i][j]-=q*a[k][j];count++;} //Subtract k-row times q
a[i][k]=0;
//k-th column below a[k][k]=0
}
}
//backward iteration to compute x[i]
for(i=N-1;i>=0;i--){
x[i]=a[i][N];count++;
for(j=N-1;j>i;j--) {
x[i]-=a[i][j]*x[j];count++;
}
printf("x[%d]=%f\n",i,x[i]);
printf("count=%d\n",count);
}
}
ガウスの消去法の問題点
• まず第一式の第一係数 a11 が0だったり、大変
小さいときは困る。もし a11 1020 のとき a11
で割ると、割らない係数が割った係数より小さ
すぎてアンダーフローしてしまう。ずっと先で
aがゼロになってもまた同じことが起こる。
kk
• ピボット選択(pivot):係数行列の第k列の第
k行目より下の中で絶対値最大のものを探し
それが第m行目にあればk行とm行を入れ替
える
正方行列aのLU分解をCrout法
を用いて行う
• Lのk列目とUのk行目を計算してすぐ、元のa行
列に上書きして行くようにすると、入力も出力
もaだけで済むのでプログラムが短くなる。
•
• #include <stdio.h>
• #include <math.h>
• #define N 3
• void crout(double a[N][N]);
•
• int main(void){
•
double a[N][N]={{2,4,6},{3,8,7},{5,7,21}};
行列の3角分解
• Ax=bを解くとき、A=LUと分解して、
Ax= LUx = Ly= b
• つまり
Ly= b を解いてから
Ux =y を解く
• どちらも三角行列だから解きやすい
3角分解の方法
• 例
2 4 6 11
3 8 7
21
5 7 21 31
2 11
3 21
5 31
0
22
32
0 1 u12
0 * 0 1
33 0 0
4 11u12
8 21u12 22
7 31u12 32
u13
u23
1
6 11u13
7 21u13 22 u23
21 31u13 32u23
よって
11 2, 21 3, 31 5, u12 2, u13 3
3角分解の方法
• 例
2 4 6 11 0 0 u11 u12 u13
3 8 7
0 * 0 u 22 u 23
21
22
5 7 21 31 32 33 0 0 u 33
2 11u11
4 11u12
6 11u13
3 21u11 8 21u12 22 u 22 7 21u13 22 u23
5 31u11 7 31u12 32 u22 21 31u13 32u23
u11 u22 u33 1
11 2, 21 3, 31 5, u12 2, u13 3
12変数に対して9方程式なので3つは任意に取れる。
とすると
3角分解の方法
• 以下 22 (8 21u12 ) / u22 (8 3 2) / 1 2
32 (7 31u12 ) / u22 (7 5 2) / 1 3
u23 (7 21u13 ) / 22 (7 3 3) / 2 1
u 23 (21 31u13 32 u 23 ) / u 33
(21 5 3 ( 3) ( 1)) / 1 3
2 0 0
L 3 2 0
5 3 3
1 2 3
U 0 1 1
0 0 1
これを一般化すると
• Crout法(i=1,…,n, j=2,…,n)
k=2,3,…,n に対し
k 1
kk a kk km u mk
m 1
k 1
ik a ik imu mk
11 a11
u kj (a kj km u mj ) / kk
m1
なのでこれを使って
u1 j a1 j / 11 が求められた
m 1
k 1
u kk 1
i1 a i1
• 正方行列aのLU分解をCrout法を用いて行
うとき、Lのk列目とUのk行目を計算し
てすぐ、元のa行列に上書きして行くよう
にすると、入力も出力もaだけで済むので
プログラムが短くなる。
•
#include <stdio.h>
• #include <math.h>
• #define N 3
• void crout(double a[N][N]);
•
• int main(void){
•
double a[N][N]={{2,4,6},{3,8,7},{5,7,21}};
•
crout(a);
•
return 0;
• }
•
• void crout(double a[N][N]){
•
int i, j, k;
•
for(k=0;k<N;k++){
•
• /* a[i][k]=L[i][k] for (k<=i) */
• for(i=k;i<N;i++){
•
for(j=0;j<k;j++) a[i][k]-=a[i][j] * a[j][k];
•
}
• /* a[k][j]=U[k][j] for (k<j) */
•
•
for(j=k+1;j<N;j++)
•
for(i=0;i<k;i++) a[k][j] -= a[k][i] * a[i][j];
•
for(j=k+1;j<N;j++) a[k][j] /= a[k][k];
•
•
printf("\n");
•
for(i=0; i<N; i++){
•
for(j=0; j<N; j++) printf("%f ", a[i][j]);
•
printf("\n");
•
}
•
}
• }
•
• <問題> 出力を表示せよ。(k=0の時は
入力したa行列のままであるが、その後は
k列目とk行目が順に書き換わり、最後
はLとUを出力している事に注意。)
© Copyright 2026 ExpyDoc