n元連立1次方程式
の係数行列Aが正値対称行列であるならば,
その解
を求めることは,
以下の方程式の最小値探索と同等である.
ここで,(*,*)はベクトルの内積を表す.
を直接解く代わりに,
方程式
の最小値探索により解を求める方法を
共役勾配法(conjugate gradient method:CG法)と呼ぶ.
の最小値探索が元のn元連立1次方程式を解くことと同等になることの証明を以下に示す.
まず,
を成分で表すと,
となる.最小値を持つ点では関数の偏微分値(傾き)が0となる.
で偏微分すると,右辺第1項はi=kとj=kの要素のみが残るので,
Aは対称行列であるので,
である.よって,
この式は
を成分で表示したものである.
よって,
の解は
を最小にする
と同じになる.
共役勾配法の計算手順†
共役勾配法の計算手順を以下に示す.
- 初期近似解
を適当に設定
- 初期近似解に対する残差を計算,修正方向ベクトルを
);
で初期化
- 以下の計算を収束するまで繰り返す(k=0,1,2,...)
を計算
- 修正係数
を計算
- k+1ステップの近似値を算出
- k+1の近似値に対する残差を計算
ならば収束したとして計算を終了
- 方向ベクトル
の修正係数
を計算
- k+1ステップの方向ベクトル
を計算
共役勾配法の実装†
共役勾配法のC++による実装例を以下に示す.
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
61
62
63
64
65
66
67
68
69
70
71
72
| |
int CGSolver(const vector< vector<double> > &A, const vector<double> &b, vector<double> &x, int n, int &max_iter, double &eps)
{
if(n <= 0) return 0;
vector<double> r(n), p(n), y(n);
x.assign(n, 0.0);
for(int i = 0; i < n; ++i){
double ax = 0.0;
for(int j = 0; j < n; ++j){
ax += A[i][j]*x[j];
}
r[i] = b[i]-ax;
p[i] = r[i];
}
double rr0 = dot(r, r, n), rr1;
double alpha, beta;
double e = 0.0;
int k;
for(k = 0; k < max_iter; ++k){
for(int i = 0; i < n; ++i){
y[i] = dot(A[i], p, n);
}
alpha = rr0/dot(p, y, n);
for(int i = 0; i < n; ++i){
x[i] += alpha*p[i];
r[i] -= alpha*y[i];
}
rr1 = dot(r, r, n);
e = sqrt(rr1);
if(e < eps){
k++;
break;
}
beta = rr1/rr0;
for(int i = 0; i < n; ++i){
p[i] = r[i]+beta*p[i];
}
rr0 = rr1;
}
max_iter = k+1;
eps = e;
return 1;
}
|
ここで,std::vectorの内積を求めるdot関数を定義して用いている.