In order to improve np.roots
performance on cubic equation, I try to implement 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
It works well when roots are real:
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))
But fails in the complex case :
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))
I guess that the problem is the evaluation of u
and v
by cube roots .
Theory say uv=-p/3
, but here uv=pJ/3
: (u,v)
is not a good pair of roots.
What is the best way to obtain a correct pair in all cases ?
EDIT
After @Sally post, I can precise the problem. The good pair is not always (u,v)
, it can be (u,vJ)
or (uJ,v)
. So the question could be :
- is there a straightforward method to catch the good pair ?
And ultimate : for the moment, by compiling this code with Numba
, it's 20x faster than np.roots
.
- is there a better algorithm to compute the three roots of a cubic equation ?