未能实施Cardano方法。立方根的复数

时间:2016-03-05 22:23:44

标签: python algorithm numpy math numba

为了提高立方方程的np.roots性能,我尝试实施Cardan(o) Method

def cardan(a,b,c,d):
    #"resolve P=ax^3+bx^2+cx+d=0" 
    #"x=z-b/a/3=z-z0 => P=z^3+pz+q"
    z0=b/3/a
    a2,b2 = a*a,b*b    
    p=-b2/3/a2 +c/a
    q=(b/27*(2*b2/a2-9*c/a)+d)/a
    D=-4*p*p*p-27*q*q+0j
    r=sqrt(-D/27)
    J=-0.5+0.86602540378443871j # exp(2i*pi/3)
    u=((-q+r)/2)**(1/3)
    v=((-q-r)/2)**(1/3)
    return u+v-z0,u*J+v/J-z0,u/J+v*J-z0

当根真实时它很有效:

In [2]: P=poly1d([1,2,3],True)
In [3]: roots(P)
Out[3]: array([ 3.,  2.,  1.])
In [4]: cardan(*P)
Out[4]: ((3+0j), (1+0j), (2+1.110e-16j))

但在复杂情况下失败了:

In [8]: P=poly1d([1,-1j,1j],True)
In [9]: P
Out[9]: poly1d([ 1., -1.,  1., -1.])
In [10]: roots(P)
Out[10]: array([  1.0000e+00+0.j,   7.771e-16+1.j,   7.771e-16-1.j])
In [11]: cardan(*P)
Out[11]: ((1.366+0.211j),(5.551e-17+0.577j),(-0.366-0.788j))

我想问题是多维数据集根对uv的评估。 Theoryuv=-p/3,但这里uv=pJ/3(u,v)不是一对好根。

在所有情况下获得正确配对的最佳方法是什么?

修改

@Sally发帖后,我可以解决问题。好的对并不总是(u,v),它可以是(u,vJ)(uJ,v)。所以问题可能是:

  • 是否有一种直接的方法来捕获好的一对?

最终:目前,通过使用Numba编译此代码,它比np.roots快20倍。

  • 有没有更好的算法来计算三次方程的三个根?

2 个答案:

答案 0 :(得分:2)

您正确识别了问题:复杂平面中存在三个可能的立方根值,从而产生9对((-q+r)/2)**(1/3)((-q-r)/2)**(1/3)。在这9个中,只有3对导致正确的根:即u * v = -p / 3的那些。一个简单的解决方法是用v替换v=-p/(3*u)的公式。这可能也是一种加速:除法应该比采用立方根更快。

然而u可能等于或接近于零,在这种情况下,除法变得可疑。实际上,在你的第一个例子中,它使精度稍差。这是一个数字稳健的方法:在return语句之前插入这两行。

choices = [abs(u*v*J**k+p/3) for k in range(3)]
v = v*J**choices.index(min(choices))

这循环在v的三个候选者上,选择最小化u*v+p/3的绝对值的那个。或许可以通过存储三个候选人来略微改善这里的表现,以便不必重新计算获胜者。

答案 1 :(得分:2)

由于r作为平方根之一的符号是免费的(相应uv的角色在u+v和{{1}中可互换作为二次多项式的根集合

u^3,v^3

这可以确保除数总是尽可能大,减少商中的误差,并最大限度地减少除以(接近)零可能成为问题的情况。