1変数n次代数方程式の根n個を求める
WikipediaによればDurand-Kerner法という数値解法があって(他にも色々あるようだが)n個の根を同時に求められるらしい.
簡単そうだったのでC++で実装してみた:
- http://www.komaba.utmc.or.jp/~flatline/durand_kerner20080905_cpp.txt (9/5追記:改良&デバグした版)
プリプロセッサ・シンボル DEBUG
を定義してコンパイルすると中間状態を吐き出す:
C:\home\nodakai\prog\solving\numerical>cl /EHsc /DDEBUG durand_kerner.cpp Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. durand_kerner.cpp Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:durand_kerner.exe durand_kerner.obj C:\home\nodakai\prog\solving\numerical>durand_kerner.exe > 2 1 2 x + 1 (1.83697e-016,3) (-0.5,0) (-0.5,0) (-0.5,0) > 4 4 1 4 x^2 + 4 x + 1 (3.53553,3.53553) (-3.53553,-3.53553) (1.25009,1.78544) (-1.61249,-1.1885) (0.180321,0.713706) (-0.922176,-0.445941) (-0.239485,0.274456) (-0.661103,-0.169894) (-0.400456,0.104936) (-0.561527,-0.0648694) (-0.461975,0.0400879) (-0.523501,-0.0247765) (-0.485476,0.0153125) (-0.508976,-0.00946372) (-0.494452,0.00584889) (-0.503429,-0.00361482) (-0.497881,0.00223408) (-0.50131,-0.00138074) (-0.499191,0.000853342) (-0.5005,-0.000527394) (-0.499691,0.000325948) (-0.500191,-0.000201447) (-0.499882,0.000124501) (-0.500073,-7.69458e-005) (-0.499955,4.75551e-005) (-0.500028,-2.93907e-005) (-0.499983,1.81644e-005) (-0.500011,-1.12262e-005) (-0.499983,1.81644e-005) (-0.500011,-1.12262e-005) > 1 0 0 -8 x^3 - 8 (7.79423,4.5) (-7.79423,4.5) (-1.65327e-015,-9) (5.21261,2.97149) (-4.49902,3.12369) (-0.286034,-4.69175) (3.34777,1.52822) (-2.4886,2.03586) (-0.499805,-2.68915) (2.24571,0.599639) (-1.36696,1.62039) (-0.814283,-1.80674) (1.95759,0.0628997) (-0.998038,1.70837) (-0.999563,-1.72734) (1.9999,-0.00049874) (-1,1.73208) (-1,-1.73205) (2,-1.88872e-009) (-1,1.73205) (-1,-1.73205) (2,-2.18582e-021) (-1,1.73205) (-1,-1.73205) (2,-2.18582e-021) (-1,1.73205) (-1,-1.73205)
定義しないと答えだけを表示する.
C:\home\nodakai\prog\solving\numerical>cl /EHsc durand_kerner.cpp Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. durand_kerner.cpp Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:durand_kerner.exe durand_kerner.obj C:\home\nodakai\prog\solving\numerical>durand_kerner.exe > 1 -3000 2999998 -999996000 -2999999 999999000 0 x^6 - 3000 x^5 + 3e+006 x^4 - 9.99996e+008 x^3 - 3e+006 x^2 + 9.99999e+008 x (-1,7.97467e-177) (1.99195e-180,-1.61087e-180) (999,1.17013e-127) (1001,3.33413e-128) (1000,1.01609e-133) (1,1.89929e-183) > 1 -3000 3000000 -1000000000 0 0 0 x^6 - 3000 x^5 + 3e+006 x^4 - 1e+009 x^3 (-1.90797e-112,7.19348e-112) (-1.96178e-112,-1.35934e-111) (1000,0.00266052) (1000,-0.000145985) (999.998,-0.00293325) (1.06013e-111,1.85072e-112)
= (x+1)x(x-1)(x-999)(x-1000)(x-1001) であって,1ずつ離れた根の3つ子が1000離れて存在している.ランダムな初期値&Newton法ではまず全てを見つけることはできないだろう.また であって,3重根2つを持つ.このような場合でも問題ない.
誤差の範囲内でよく動いてくれているようだ.