Durand-kerner 实现不起作用
Durand-Kerner 算法的这种实现有什么问题(此处)?
def durand_kerner(poly, start=complex(.4, .9), epsilon=10**-16):#float('-inf')):
roots = []
for e in xrange(poly.degree):
roots.append(start ** e)
while True:
new = []
for i, r in enumerate(roots):
new_r = r - (poly(r))/(reduce(operator.mul, [(r - r_1) for j, r_1 in enumerate(roots) if i != j]))
new.append(new_r)
if all(n == roots[i] or abs(n - roots[i]) < epsilon for i, n in enumerate(new)):
return new
roots = new
当我尝试时,我必须使用 KeyboardInterrupt
来停止它,因为它不会停止!poly
是 pypol
库的多项式实例。
先感谢您, rubik
编辑:使用 numpy 多项式需要 9 次迭代:
In [1]: import numpy as np
In [2]: roots.d1(np.poly1d([1, -3, 3, -5]))
3
[(1.3607734793516519+2.0222302921553128j), (-1.3982133295376746-0.69356635962504309j), (3.0374398501860234-1.3286639325302696j)]
[(0.98096328371966801+1.3474626910848715j), (-0.3352519326012724-0.64406860772816388j), (2.3542886488816044-0.70339408335670761j)]
[(0.31718054925650596+0.93649454851955749j), (0.49001572078718736-0.9661410790307261j), (2.1928037299563066+0.029646530511168612j)]
[(0.20901563897345796+1.5727420147652911j), (0.041206038662691125-1.5275192097633465j), (2.7497783223638508-0.045222805001944255j)]
[(0.21297050700971876+1.3948274731404162j), (0.18467846583682396-1.3845653821841168j), (2.6023510271534573-0.010262090956299326j)]
[(0.20653075193800668+1.374878742771485j), (0.20600107336130213-1.3746529207714699j), (2.5874681747006911-0.00022582200001499547j)]
[(0.20629950692533283+1.3747296033941407j), (0.20629947661265013-1.374729584400741j), (2.5874010164620169-1.899339978055233e-08j)]
[(0.20629947401589896+1.3747296369986031j), (0.20629947401590082-1.3747296369986042j), (2.5874010519682002+9.1830687539942581e-16j)]
[(0.20629947401590029+1.3747296369986026j), (0.20629947401590026-1.3747296369986026j), (2.5874010519681994+1.1832913578315177e-30j)]
Out[2]:
[(0.20629947401590029+1.3747296369986026j),
(0.20629947401590029-1.3747296369986026j),
(2.5874010519681994+0j)]
使用 pypol 多项式它永远不会完成(这可能是 pypol 中的错误):
In [3]: roots.d2(poly1d([1, -3, 3, -5]))
^C---------------------------------------------------------------------------
KeyboardInterrupt
但我找不到错误!
EDIT2:将 __call__
方法与 Martin 的 Poly 进行比较:
>>> p = Poly(-5, 3, -3, 1)
>>> from pypol import poly1d
>>> p2 = poly1d([1, -3, 3, -5])
>>> for i in xrange(-100000, 100000):
assert p(i) == p2(i)
>>>
>>> for i in xrange(-10000, 10000):
assert p(complex(1, i)) == p2(complex(1, i))
>>> for i in xrange(-10000, 10000):
assert p(complex(i, i)) == p2(complex(i, i))
>>>
EDIT3:如果根不是复数,则 pypol 可以正常工作:
In [1]: p = pypol.funcs.from_roots([4, -2, 443, -11212])
In [2]: durand_kerner(p)
Out[2]: [(4+0j), (443+0j), (-2+0j), (-11212+0j)]
所以它不会仅当根为复数时才有效!
EDIT4:我为 numpy 多项式编写了一个稍微不同的实现,并发现在一次迭代后(维基百科多项式的根)不同:
In [4]: d1(numpyp.poly1d([1, -3, 3, -5]))
Out[4]:
[(0.98096328371966801+1.3474626910848715j),
(-0.3352519326012724-0.64406860772816388j),
(2.3542886488816044-0.70339408335670761j)]
In [5]: d2(pypol.poly1d([1, -3, 3, -5]))
Out[5]:
[(0.9809632837196679+1.3474626910848717j),
(-0.33525193260127306-0.64406860772816377j),
(2.3542886488816048-0.70339408335670772j)] ## here
EDIT5:嘿!如果我将行: if all(n == root[i] ... )
更改为 if all(str(n) == str(roots[i]) ... )
它完成并返回正确的根!!!
In [9]: p = pypol.poly1d([1, -3, 3, -5])
In [10]: roots.durand_kerner(p)
Out[10]:
[(0.20629947401590029+1.3747296369986026j),
(0.20629947401590013-1.3747296369986026j),
(2.5874010519681994+0j)]
但问题是:为什么它适用于不同的复数比较?
更新
现在它可以工作了,我已经做了一些测试:
In [1]: p = pypol.poly1d([1, -3, 3, -1])
In [2]: p
Out[2]: + x^3 - 3x^2 + 3x - 1
In [3]: pypol.roots.cubic(p)
Out[3]: (1.0, 1.0, 1.0)
In [4]: durand_kerner(p)
Out[4]:
((1+0j),
(1.0000002484566535-2.708692281244913e-17j),
(0.99999975147728026+2.9792265510301965e-17j))
In [5]: q = x ** 3 - 1
In [6]: q
Out[6]: + x^3 - 1
In [7]: pypol.roots.cubic(q)
Out[7]: (1.0, (-0.5+0.8660254037844386j), (-0.5-0.8660254037844386j))
In [8]: durand_kerner(q)
Out[8]: ((1+0j), (-0.5-0.8660254037844386j), (-0.5+0.8660254037844386j))
What's wrong with this implementation of the Durand-Kerner algorithm (here) ?
def durand_kerner(poly, start=complex(.4, .9), epsilon=10**-16):#float('-inf')):
roots = []
for e in xrange(poly.degree):
roots.append(start ** e)
while True:
new = []
for i, r in enumerate(roots):
new_r = r - (poly(r))/(reduce(operator.mul, [(r - r_1) for j, r_1 in enumerate(roots) if i != j]))
new.append(new_r)
if all(n == roots[i] or abs(n - roots[i]) < epsilon for i, n in enumerate(new)):
return new
roots = new
When I try it, I have to stop it with KeyboardInterrupt
because it doesn't stop!poly
is a Polynomial instance of the pypol
library.
Thank you in advance,
rubik
EDIT: Using a numpy polynomial it takes 9 iterations:
In [1]: import numpy as np
In [2]: roots.d1(np.poly1d([1, -3, 3, -5]))
3
[(1.3607734793516519+2.0222302921553128j), (-1.3982133295376746-0.69356635962504309j), (3.0374398501860234-1.3286639325302696j)]
[(0.98096328371966801+1.3474626910848715j), (-0.3352519326012724-0.64406860772816388j), (2.3542886488816044-0.70339408335670761j)]
[(0.31718054925650596+0.93649454851955749j), (0.49001572078718736-0.9661410790307261j), (2.1928037299563066+0.029646530511168612j)]
[(0.20901563897345796+1.5727420147652911j), (0.041206038662691125-1.5275192097633465j), (2.7497783223638508-0.045222805001944255j)]
[(0.21297050700971876+1.3948274731404162j), (0.18467846583682396-1.3845653821841168j), (2.6023510271534573-0.010262090956299326j)]
[(0.20653075193800668+1.374878742771485j), (0.20600107336130213-1.3746529207714699j), (2.5874681747006911-0.00022582200001499547j)]
[(0.20629950692533283+1.3747296033941407j), (0.20629947661265013-1.374729584400741j), (2.5874010164620169-1.899339978055233e-08j)]
[(0.20629947401589896+1.3747296369986031j), (0.20629947401590082-1.3747296369986042j), (2.5874010519682002+9.1830687539942581e-16j)]
[(0.20629947401590029+1.3747296369986026j), (0.20629947401590026-1.3747296369986026j), (2.5874010519681994+1.1832913578315177e-30j)]
Out[2]:
[(0.20629947401590029+1.3747296369986026j),
(0.20629947401590029-1.3747296369986026j),
(2.5874010519681994+0j)]
Using a pypol polynomial it never finishes (it is probably a bug in pypol):
In [3]: roots.d2(poly1d([1, -3, 3, -5]))
^C---------------------------------------------------------------------------
KeyboardInterrupt
but I can't find the bug!!
EDIT2: Comparing the __call__
method with Martin's Poly:
>>> p = Poly(-5, 3, -3, 1)
>>> from pypol import poly1d
>>> p2 = poly1d([1, -3, 3, -5])
>>> for i in xrange(-100000, 100000):
assert p(i) == p2(i)
>>>
>>> for i in xrange(-10000, 10000):
assert p(complex(1, i)) == p2(complex(1, i))
>>> for i in xrange(-10000, 10000):
assert p(complex(i, i)) == p2(complex(i, i))
>>>
EDIT3: pypol works fine if the roots aren't complex numbers:
In [1]: p = pypol.funcs.from_roots([4, -2, 443, -11212])
In [2]: durand_kerner(p)
Out[2]: [(4+0j), (443+0j), (-2+0j), (-11212+0j)]
So it doesn't works only when the roots are complex numbers!
EDIT4: I wrote a slightly different implementation for numpy polynomials and saw that after one iteration the roots (of the Wikipedia polynomial) are different:
In [4]: d1(numpyp.poly1d([1, -3, 3, -5]))
Out[4]:
[(0.98096328371966801+1.3474626910848715j),
(-0.3352519326012724-0.64406860772816388j),
(2.3542886488816044-0.70339408335670761j)]
In [5]: d2(pypol.poly1d([1, -3, 3, -5]))
Out[5]:
[(0.9809632837196679+1.3474626910848717j),
(-0.33525193260127306-0.64406860772816377j),
(2.3542886488816048-0.70339408335670772j)] ## here
EDIT5: Hey! If I change the line: if all(n == roots[i] ... )
into if all(str(n) == str(roots[i]) ... )
it finishes and returns the right roots!!!
In [9]: p = pypol.poly1d([1, -3, 3, -5])
In [10]: roots.durand_kerner(p)
Out[10]:
[(0.20629947401590029+1.3747296369986026j),
(0.20629947401590013-1.3747296369986026j),
(2.5874010519681994+0j)]
But the question is: why does it work with a different complex numbers comparation??
UPDATE
Now it works, and I've done some tests:
In [1]: p = pypol.poly1d([1, -3, 3, -1])
In [2]: p
Out[2]: + x^3 - 3x^2 + 3x - 1
In [3]: pypol.roots.cubic(p)
Out[3]: (1.0, 1.0, 1.0)
In [4]: durand_kerner(p)
Out[4]:
((1+0j),
(1.0000002484566535-2.708692281244913e-17j),
(0.99999975147728026+2.9792265510301965e-17j))
In [5]: q = x ** 3 - 1
In [6]: q
Out[6]: + x^3 - 1
In [7]: pypol.roots.cubic(q)
Out[7]: (1.0, (-0.5+0.8660254037844386j), (-0.5-0.8660254037844386j))
In [8]: durand_kerner(q)
Out[8]: ((1+0j), (-0.5-0.8660254037844386j), (-0.5+0.8660254037844386j))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的算法看起来不错,它对我有用,维基百科中的示例
给出了
Your algorithm looks fine, and it works for me for the example in the wikipedia
gives
关于您的“EDIT 5”:发生这种情况是因为 str() 没有将数字格式化为完整精度。
所以不要这样做。
无论如何,代码中的相等性测试:
是多余的;如果
n == root[i]
,那么abs(n - root[i])
将为零,所以你可以做只是并投入一些努力来工作epsilon 的默认值应该是什么;正如我在评论中指出的,解决
X**3 == 1
收敛,但您的默认 epsilon 太小,无法永远阻止它循环。1.12e-16
看起来对于默认 epsilon 来说是更好的选择。为了娱乐,尝试不适合的 Poly([-1, 3, -3, 1]) ...三个根都等于 (1+0j) ...它需要超过 600 次迭代,并且最后 10 次或因此迭代会惊人地跳跃,直到它从左域的远处得出一个合理的解决方案。
About your "EDIT 5": this happens because str() doesn't format numbers to the full precision.
So don't do that.
In any case, the equality test in your code:
is redundant; if
n == roots[i]
, thenabs(n - roots[i])
will be zero, so you could do justand put a bit of effort into working out what the default for
epsilon
should be; as I pointed out in a comment, solvingX**3 == 1
converges but your default epsilon is too small to stop it looping forever.1.12e-16
looks like a better bet for the default epsilon.For amusement, try the unsuited Poly([-1, 3, -3, 1]) ... three roots all equal to (1+0j) ... it takes over 600 iterations, and the errors in the last 10 or so iterations jump about astonishingly until it just arrives at a reasonable solution from far out in left field.