next up previous
Next: この文書について... Up: 数理解析特論 連立 次方程式に対する CG Previous: A. CG 法の計算手順について補足

B. 叩き台プログラムのリスト

以下に cg.c を掲げる。

   1 /*
   2  * cg.c --- CG 法のサンプル・プログラム
   3  *   コンパイル: gcc -O -o cg cg.c -lm
   4  */
   5 
   6 #include <stdio.h>
   7 #include <math.h>
   8 #include <stdlib.h>
   9 
  10 typedef double *vector;
  11 typedef vector *matrix;
  12 
  13 vector new_vector(int n)   { return malloc(sizeof(double) * n); }
  14 void free_vector(vector v) { free(v); }
  15 
  16 matrix new_matrix(int nrow, int ncol)
  17 {
  18     int i;
  19     vector ap;
  20     matrix a;
  21 
  22     if ((a = malloc(nrow * sizeof(vector))) == NULL)
  23         return NULL;
  24     if ((ap = malloc(nrow * ncol * sizeof(double))) == NULL) {
  25         free(a); return NULL;
  26     }
  27     for (i = 0; i < nrow; i++)
  28         a[i] = ap + (i * ncol);
  29     return a;
  30 }
  31 
  32 void free_matrix(matrix a)
  33 {
  34     free(a[0]);
  35     free(a);
  36 }
  37 
  38 /* m行n列の零行列を作って返す */
  39 matrix zeros(int m, int n)
  40 {
  41   int i, j;
  42   matrix tmp;
  43   tmp = new_matrix(m, n);
  44   for (i = 0; i < m; i++)
  45     for (j = 0; j < n; j++)
  46       tmp[i][j] = 0.0;
  47   return tmp;
  48 }
  49 
  50 /* n次元のベクトル z を零ベクトルにする */
  51 void clear_vector(int n, vector z)
  52 {
  53   int i;
  54   for (i = 0; i < n; i++)
  55     z[i] = 0.0;
  56 }
  57 
  58 // n 次元ベクトルの内容を表示する
  59 void print_vector(int n, vector x)
  60 {
  61   int i;
  62   for (i = 0; i < n; i++) {
  63     printf("  %g", x[i]);
  64     if (i % 5 == 4)
  65       printf("\n");
  66   }
  67   if (i % 5 != 0)
  68     printf("\n");
  69 }
  70 
  71 // m行n列の行列の内容を表示する
  72 void print_matrix(int m, int n, matrix a)
  73 {
  74   int i;
  75   for (i = 0; i < m; i++)
  76     print_vector(n, a[i]);
  77 }
  78 
  79 // ベクトル x, y の内積 (x,y) を計算する
  80 double dotproduct(int n, vector x, vector y)
  81 {
  82   int i;
  83   double sum;
  84   sum = 0;
  85   for (i = 0; i < n; i++)
  86     sum += x[i] * y[i];
  87   return sum;
  88 }
  89 
  90 // ベクトル x のノルム ||x|| を計算する
  91 double norm(int n, vector x)
  92 {
  93   return sqrt(dotproduct(n, x, x));
  94 }
  95 
  96 // 行列 A, ベクトル b の積 A b を計算して、ベクトル Ab に書き込む
  97 void multiply_mv(int m, int n, vector Ab, matrix A, vector b)
  98 {
  99   int i;
 100   for (i = 0; i < m; i++)
 101       Ab[i] = dotproduct(n, A[i], b);
 102 }
 103 
 104 // ベクトル x をベクトル y にコピーする
 105 void copy_vector(int n, vector y, vector x)
 106 {
 107   int i;
 108   for (i = 0; i < n; i++)
 109     y[i] = x[i];
 110 }
 111 
 112 /* CG 法で n 元連立1次方程式 A x=b を解く。
 113  * 呼び出すときの x は初期ベクトル, 戻ってきたときの x は解となる。
 114  * ||残差|| < ε ||b|| となったら反復を停止する。
 115  * 反復回数を maxiter に返す。
 116  */
 117 
 118 void cg(int n, matrix A, vector b, vector x, double eps, int *maxiter)
 119 {
 120   int i, k;
 121   double eps_b, pAp, alpha, beta;
 122   vector r, p, Ap;
 123 
 124   r = new_vector(n);
 125   p = new_vector(n);
 126   Ap = new_vector(n);
 127   /* eps_b := ε ||b|| */
 128   eps_b = eps * norm(n, b);
 129   /* r := A x */
 130   multiply_mv(n, n, r, A, x);
 131   /* r := b - A x */
 132   for (i = 0; i < n; i++)
 133     r[i] = b[i] - r[i];
 134   /* p := r */
 135   copy_vector(n, p, r);
 136   /* 反復 */
 137   for (k = 0; k < n; k++) {
 138     /* Ap := A * p */
 139     multiply_mv(n, n, Ap, A, p);
 140     /* pAp := (p, Ap) */
 141     pAp = dotproduct(n, p, Ap);
 142     /* α = (r,p)/(p,Ap) */
 143     alpha = dotproduct(n, r, p) / pAp;
 144     /* x, r の更新 */
 145     for (i = 0; i < n; i++) {
 146       /* x = x + α*p */
 147       x[i] += alpha * p[i];
 148       /* r = r - α*A*p */
 149       r[i] -= alpha * Ap[i];
 150     }
 151     /* ||r||<ε||b|| ならば反復を終了 */
 152     if (norm(n, r) < eps_b)
 153       break;
 154     /* β := - (r, Ap) / (p, Ap) */
 155     beta = - dotproduct(n, r, Ap) / pAp;
 156     /* p := r + β*p */
 157     for (i = 0; i < n; i++)
 158       p[i] = r[i] + beta * p[i];
 159   }
 160   if (maxiter != NULL)
 161     *maxiter = k;
 162 }
 163 
 164 int main()
 165 {
 166   int n, N, i, niter;
 167   matrix a;
 168   vector x, b;
 169   double c1, c2;
 170   /* 問題のサイズを決める */
 171   printf("c1, c2, N=");
 172   scanf("%lf %lf %d", &c1, &c2, &N);
 173   n = N / 2;
 174   N = 2 * n;
 175   /* 行列 A, ベクトル x, b を記憶する変数の準備 */
 176   a = zeros(N, N);
 177   x = new_vector(N);
 178   b = new_vector(N);
 179   /* 係数行列を決める */
 180   for (i = 0; i < N; i++) {
 181     a[i][i] = 4.0;
 182     if (i != 0)
 183       a[i][i-1] = -1.0;
 184     if (i != N-1)
 185       a[i][i+1] = -1.0;
 186   }
 187   a[n-1][n] = 0.0;
 188   a[n][n-1] = 0.0;
 189   /* */
 190   for (i = 0; i < n; i++) {
 191     if (i != 0)
 192       a[i][i-1] *= c1;
 193     a[i][i] *= c1;
 194     a[i][i+1] *= c1;
 195   }
 196   for (i = n; i < N; i++) {
 197     a[i][i-1] *= c2;
 198     a[i][i] *= c2;
 199     if (i != N-1)
 200       a[i][i+1] *= c2;
 201   }
 202   /* 係数行列を表示する */
 203   if (N < 10) {
 204     printf("a=\n");
 205     print_matrix(N, N, a);
 206   }
 207   /* 解 x を決める */
 208   for (i = 0; i < N; i++)
 209     x[i] = i;
 210   /* 解に対応する右辺 b を計算する */
 211   multiply_mv(N, N, b, a, x);
 212   /* b を表示する */
 213   if (N < 10) {
 214     printf("b=\n");
 215     print_vector(N, b);
 216   }
 217   /* 初期ベクトル x を零ベクトルにする */
 218   clear_vector(N, x);
 219   /* CG 法で解く */
 220   cg(N, a, b, x, 1e-12, &niter);
 221   /* 解を表示する */
 222   printf("x=\n");
 223   print_vector(N, x);
 224   printf("反復回数=%d\n", niter);
 225 
 226   return 0;
 227 }


next up previous
Next: この文書について... Up: 数理解析特論 連立 次方程式に対する CG Previous: A. CG 法の計算手順について補足
Masashi Katsurada
平成17年5月24日