ガウスの消去法における前進消去の式をもう一度みてみる.
右辺の第2項に
で割るという計算が含まれている.
元々対角要素に0を含んでいる,もしくは,前進消去の過程で対角要素に0が出てくるとその時点で計算が止まってしまう.
また,0でなくても絶対値が非常に小さい数だと桁落ちが起こり,誤差が増大する原因ともなる.
この問題に対処するために,ピボット(pivot:枢軸)選択(pivotal elimination method),もしくは,ピボッティング(pivoting)
と呼ばれる方法を説明する.
まず,連立方程式において式の順番は交換可能であることに注目する.
つまり,
行目の処理を行う際,その対角要素
と同じ列でまだ処理していない要素
の値を調べ,その絶対値が最大の行と
行を入れ替えた後で前進消去の式を適用する.
これがピボット選択である.
ちなみに,このように行の入れ替えだけ行うことを部分的ピボッティング(partial pivoting)と呼び,
さらに列方向についても調べて入れ替えを適用することを完全ピボッティング(complete pivoting)と呼ぶ.
ただし,部分的ピボッティングでは未知数の順番は変わらないが,
完全ピボッティングの場合は未知数の順番も変化するので注意.
以下にピボット選択を含めたガウス消去法のコード例を示す.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| |
inline void Pivoting(vector< vector<double> > &A, int n, int k)
{
int p = k; double am = fabs(A[k][k]); for(int i = k+1; i < n; ++i){
if(fabs(A[i][k]) > am){
p = i;
am = fabs(A[i][k]);
}
}
if(k != p) swap(A[k], A[p]);
}
int GaussEliminationWithPivoting(vector< vector<double> > &A, int n)
{
for(int k = 0; k < n-1; ++k){
Pivoting(A, n, k);
double akk = A[k][k];
for(int i = k+1; i < n; ++i){
double aik = A[i][k];
for(int j = k; j < n+1; ++j){
A[i][j] = A[i][j]-aik*(A[k][j]/akk);
}
}
}
A[n-1][n] = A[n-1][n]/A[n-1][n-1];
for(int i = n-2; i >= 0; --i){
double ax = 0.0;
for(int j = i+1; j < n; ++j){
ax += A[i][j]*A[j][n];
}
A[i][n] = (A[i][n]-ax)/A[i][i];
}
return 1;
}
|