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 }