[What I want] is to find the only one least positive real root of the quadratic function a x ^ 4 + b x ^ 3 + c x ^ 2 + d x + e
[Existing method] My equation for collision prediction, the maximum degree is the quartic function as f (x) = a x ^ 4 + b x ^ 3 + c x ^ 2 + d x + e and a, b, c, d, e can be positive / negative / zero ( real float value ). So my function f (x) can be quartic, cubic or quadratic depending on the input coefficient a, b, c, d, e.
I am currently using numpy to search for root as shown below.
import numpy
root_output = numpy.roots ([a, b, c, d, e])
The " root_output " from the numpy module can be all possible real / complex roots depending on the input factor. So I have to look one by one at “ root_output ” and check which root is the smallest real positive value (root> 0?)
[Problem] My program needs to execute numpy.roots ([a, b, c, d, e]) many times, so many times the execution of numpy.roots is too slow for my project. and (a, b, c, d, e) always changes every time numpy.roots is executed
My attempt is to run the code on the Raspberry Pi2. The following is an example of processing time.
- Running many times numpy.roots on PC: 1.2 seconds
- Runs many times numpy.roots on raspberry Pi2: 17 seconds
Could you advise me to find the smallest positive real root in the quickest solution ? Using scipy.optimize or implementing some algorithm to speed up the search for the root or any advice from you will be great.
Thanks.
[Decision]
A quadratic function needs only real positive roots (please remember division by zero)
def SolvQuadratic(a, b ,c): d = (b**2) - (4*a*c) if d < 0: return [] if d > 0: square_root_d = math.sqrt(d) t1 = (-b + square_root_d) / (2 * a) t2 = (-b - square_root_d) / (2 * a) if t1 > 0: if t2 > 0: if t1 < t2: return [t1, t2] return [t2, t1] return [t1] elif t2 > 0: return [t2] else: return [] else: t = -b / (2*a) if t > 0: return [t] return []
Quartic Function for apartment function, you can use pure python / numba version as below answer from @BM . I also add another version of cython from BB code. You can use the code below as a .pyx file and then compile it to get about 2 times faster than pure python (note the rounding problems).
import cmath cdef extern from "complex.h": double complex cexp(double complex) cdef double complex J=cexp(2j*cmath.pi/3) cdef double complex Jc=1/J cdef Cardano(double a, double b, double c, double d): cdef double z0 cdef double a2, b2 cdef double p ,q, D cdef double complex r cdef double complex u, v, w cdef double w0, w1, w2 cdef double complex r1, r2, r3 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 r=cmath.sqrt(-D/27+0j) u=((-qr)/2)**0.33333333333333333333333 v=((-q+r)/2)**0.33333333333333333333333 w=u*v w0=abs(w+p/3) w1=abs(w*J+p/3) w2=abs(w*Jc+p/3) if w0<w1: if w2<w0 : v = v*Jc elif w2<w1 : v = v*Jc else: v = v*J r1 = u+v-z0 r2 = u*J+v*Jc-z0 r3 = u*Jc+v*J-z0 return r1, r2, r3 cdef Roots_2(double a, double complex b, double complex c): cdef double complex bp cdef double complex delta cdef double complex r1, r2 bp=b/2 delta=bp*bp-a*c r1=(-bp-delta**.5)/a r2=-r1-b/a return r1, r2 def SolveQuartic(double a, double b, double c, double d, double e): "Ferrarai Method" "resolution of P=ax^4+bx^3+cx^2+dx+e=0, coeffs reals" "First shift : x= zb/4/a => P=z^4+pz^2+qz+r" cdef double z0 cdef double a2, b2, c2, d2 cdef double p, q, r cdef double A, B, C, D cdef double complex y0, y1, y2 cdef double complex a0, b0 cdef double complex r0, r1, r2, r3 z0=b/4.0/a a2,b2,c2,d2 = a*a,b*b,c*c,d*d p = -3.0*b2/(8*a2)+c/a q = b*b2/8.0/a/a2 - 1.0/2*b*c/a2 + d/a r = -3.0/256*b2*b2/a2/a2 + c*b2/a2/a/16 - b*d/a2/4+e/a "Second find y so P2=Ay^3+By^2+Cy+D=0" A=8.0 B=-4*p C=-8*r D=4*r*pq*q y0,y1,y2=Cardano(A,B,C,D) if abs(y1.imag)<abs(y0.imag): y0=y1 if abs(y2.imag)<abs(y0.imag): y0=y2 a0=(-p+2*y0)**.5 if a0==0 : b0=y0**2-r else : b0=-q/2/a0 r0,r1=Roots_2(1,a0,y0+b0) r2,r3=Roots_2(1,-a0,y0-b0) return (r0-z0,r1-z0,r2-z0,r3-z0)
[The problem of the Ferrari method] We encountered a problem when the coefficients of the apartment equation are [0.00614656, -0.0933333333333, 0.527664995846, -1.31617928376, 1.21906444869] the output from numpy.roots and ferrari methods are completely different (numpy.roots is the correct conclusion).
import numpy as np import cmath J=cmath.exp(2j*cmath.pi/3) Jc=1/J def ferrari(a,b,c,d,e): "Ferrarai Method" "resolution of P=ax^4+bx^3+cx^2+dx+e=0, coeffs reals" "First shift : x= zb/4/a => P=z^4+pz^2+qz+r" z0=b/4/a a2,b2,c2,d2 = a*a,b*b,c*c,d*d p = -3*b2/(8*a2)+c/a q = b*b2/8/a/a2 - 1/2*b*c/a2 + d/a r = -3/256*b2*b2/a2/a2 +c*b2/a2/a/16-b*d/a2/4+e/a "Second find y so P2=Ay^3+By^2+Cy+D=0" A=8 B=-4*p C=-8*r D=4*r*pq*q y0,y1,y2=Cardano(A,B,C,D) if abs(y1.imag)<abs(y0.imag): y0=y1 if abs(y2.imag)<abs(y0.imag): y0=y2 a0=(-p+2*y0)**.5 if a0==0 : b0=y0**2-r else : b0=-q/2/a0 r0,r1=Roots_2(1,a0,y0+b0) r2,r3=Roots_2(1,-a0,y0-b0) return (r0-z0,r1-z0,r2-z0,r3-z0) #~ @jit(nopython=True) def Cardano(a,b,c,d): 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 r=cmath.sqrt(-D/27+0j) u=((-qr)/2)**0.33333333333333333333333 v=((-q+r)/2)**0.33333333333333333333333 w=u*v w0=abs(w+p/3) w1=abs(w*J+p/3) w2=abs(w*Jc+p/3) if w0<w1: if w2<w0 : v*=Jc elif w2<w1 : v*=Jc else: v*=J return u+v-z0, u*J+v*Jc-z0, u*Jc+v*J-z0 #~ @jit(nopython=True) def Roots_2(a,b,c): bp=b/2 delta=bp*bp-a*c r1=(-bp-delta**.5)/a r2=-r1-b/a return r1,r2 coef = [0.00614656, -0.0933333333333, 0.527664995846, -1.31617928376, 1.21906444869] print("Coefficient A, B, C, D, E", coef) print("") print("numpy roots: ", np.roots(coef)) print("") print("ferrari python ", ferrari(*coef))