B.6.1 ソースプログラム DK3.C


   1 // DK3.cpp -- DKA 法で代数方程式の根を求める
   2 //
   3 //   最初のバージョン DK3.C は 2000年頃に書かれた。
   4 //   古い C++ で書かれていたので少し書き換え (2019/1/12)。
   5 //
   6 //   g++ -I/usr/X11/include DK3.cpp -L/usr/local/lib -lglscd -L/usr/X11/lib -lX11
   7 
   8 #include <iostream>  // cout, cin, cerr など
   9 #include <iomanip>   // setprecision() のため
  10 #include <cmath>
  11 #include <cstdlib>   // exit()
  12 #include <complex>   // complex<double> のため
  13 
  14 // GLSC のヘッダーファイルをインクルード
  15 extern "C" {
  16 #define G_DOUBLE
  17 #include <glsc.h>
  18 };
  19 
  20 // 毎度 std::complex<double> と書くのは面倒なので
  21 typedef std::complex<double> dcomplex;
  22 
  23 // プロトタイプ宣言
  24 dcomplex polynomial(int, dcomplex *, dcomplex);
  25 dcomplex bunbo(int, dcomplex *, int);
  26 
  27 // 解きたい代数方程式の次数の最高値
  28 #define MAXN (100)
  29 
  30 int main(void)
  31 {
  32   int i, k, n;
  33   dcomplex a[MAXN+1], x[MAXN], newx[MAXN], dx[MAXN];
  34   dcomplex g, I(0,1);
  35   double r0, R, max, pi;
  36 
  37   // 数学定数 (円周率) の準備
  38   pi = 4 * atan(1.0);
  39 
  40   // 表\示の桁数を 16 桁にする
  41   std::cout << std::setprecision(16);
  42 
  43   // 方程式の入力
  44   std::cout << "次数 n を入力してください (1≦n≦" << MAXN << "): ";
  45   std::cin >> n;
  46   if (n > MAXN || n <= 0) {
  47     std::cerr << "次数は" << MAXN << "以下の自然数として下さい。" << std::endl;
  48     std::exit(0);
  49   }
  50   for (i = 0; i <= n; i++) {
  51     std::cout << (n-i) << "次の係数を入力してください: ";
  52     std::cin >> a[i];
  53   }
  54 
  55   // 多項式を最高次の係数で割って monic にする
  56   std::cout << "monic にします。" << std::endl;
  57   for (i = 1; i <= n; i++)
  58     a[i] /= a[0];
  59   a[0] = 1;
  60   std::cout << "修正した係数" << std::endl;
  61   for (i = 0; i <= n; i++)
  62     std::cout << "a[" << i << "]=" << a[i] << std::endl;
  63 
  64   // Aberth の初期値を配置する円の決定
  65   g = - a[1] / (dcomplex)n;
  66   std::cout << "根の重心" << g << std::endl;
  67   max = 0;
  68   for (i = 1; i <= n; i++)
  69     if (abs(a[i]) > max)
  70       max = abs(a[i]);
  71   std::cout << "max|a_i|=" << max << std::endl;
  72   r0 = abs(g) + 1 + max;
  73   std::cout << "根は重心中心で、半径 r0=" << r0 << "の円盤内にある" << std::endl;
  74 
  75   std::cout << "円の半径 (分からなければ上の値を指定してください): ";
  76   R = r0;
  77   std::cin >> r0;
  78   if (r0 > R)
  79     R = r0;
  80   std::cout << "図は根の重心を中心として半径 " << R
  81             << "の円が表示できるようにします。" << std::endl;
  82 
  83   // Aberth の初期値
  84   std::cout << "初期値" << std::endl;
  85   for (i = 0; i < n; i++) {
  86     double theta;
  87     theta = 2 * i * pi / n + pi / (2 * n);
  88     x[i] = g + r0 * exp(I * theta);
  89     std::cout << x[i] << std::endl;
  90   }
  91 
  92   // グラフィックス・ライブラリィ GLSC の初期化
  93   g_init((char *)"DKGRAPH", 120.0, 120.0);
  94   g_device(G_BOTH);
  95   // 座標系の指定
  96   g_def_scale(0,
  97 	      real(g) - 1.1 * R, real(g) + 1.1 * R,
  98 	      imag(g) - 1.1 * R, imag(g) + 1.1 * R,
  99 	      10.0, 10.0, 100.0, 100.0);
 100   g_sel_scale(0);
 101   // 線種、マーカーの指定
 102   g_def_line(0, G_BLACK, 0, G_LINE_SOLID);
 103   g_sel_line(0);
 104   g_def_marker(0, G_RED, 2, G_MARKER_CIRC);
 105   g_sel_marker(0);
 106 
 107   // 初期値と初期値を置く円を描く
 108   g_circle(real(g), imag(g), r0, G_YES, G_NO);
 109   for (i = 0; i < n; i++)
 110     g_marker(real(x[i]), imag(x[i]));
 111 
 112   // 反復
 113   for (k = 1; k <= 1000; k++) {
 114     double error;
 115     std::cout << "第" << k << "反復" << std::endl;
 116     for (i = 0; i < n; i++) {
 117       dx[i] = polynomial(n, a, x[i]) / bunbo(n, x, i);
 118       newx[i] = x[i] - dx[i];
 119       // 図示する
 120       g_move(real(x[i]), imag(x[i]));
 121       g_plot(real(newx[i]), imag(newx[i]));
 122       g_marker(real(newx[i]), imag(newx[i]));
 123     }
 124     // 更新
 125     for (i = 0; i < n; i++) {
 126       x[i] = newx[i];
 127       std::cout << x[i] << std::endl;
 128     }
 129     // 変化量を計算する (ここは非常に素朴)
 130     error = 0.0;
 131     for (i = 0; i < n; i++)
 132       error += abs(dx[i]);
 133     std::cout << "変化量=" << error << std::endl;
 134     // 変化量が小さければ収束したと判断して反復を終了する。
 135     if (error < 1e-12)
 136       break;
 137   }
 138   // GLSC 終了の処理
 139   std::cout << "終了にはグラフィックスのウィンドウをクリック。" << std::endl;
 140   g_sleep(-1.0);
 141   g_term();
 142 
 143   return 0;
 144 }
 145 
 146 // 多項式の値の計算 (Horner 法)
 147 dcomplex polynomial(int n, dcomplex *a, dcomplex z)
 148 {
 149   int i;
 150   dcomplex w;
 151   w = a[0];
 152   for (i = 1; i <= n; i++)
 153     w = (w * z + a[i]);
 154   return w;
 155 }
 156 
 157 //  Π (zi-zj) の計算
 158 // j≠i
 159 dcomplex bunbo(int n,
 160 		      dcomplex *z,
 161 		      int i)
 162 {
 163   int j;
 164   dcomplex w(1,0);
 165   for (j = 0; j < n; j++)
 166     if (j != i)
 167       w *= (z[i] - z[j]);
 168   return w;
 169 }



桂田 祐史