python中求四次多项式4次最小正实根的最快方法
[我想要的]是找到四次函数ax^4+bx^3+cx^2+dx+e [现有方法] 我的方程用于碰撞预测,最大阶数是四次函数,如f(x)=ax^4+bx^3+cx^2+dx+e 和a、b、c、d、ecoef可以是正/负/零(实际浮点值)。所以我的函数f(x)可以是四次、三次或二次函数,具体取决于a、b、c、d、e输入系数 目前我使用numpy查找root,如下所示 进口numpy root_output=numpy.root([a,b,c,d,e]) 来自numpy模块的“根\u输出”可以是所有可能的实/复数根,具体取决于输入系数。因此,我必须逐个查看“root\u输出”,并检查哪个根是最小的实际正值(root>0?) [问题] 我的程序需要多次执行numpy.roots([a,b,c,d,e])执行numpy.roots的次数太多,对我的项目来说太慢了。每次执行numpy.roots时,(a,b,c,d,e)值都会更改 我的尝试是在Raspberry Pi2上运行代码。下面是处理时间的示例python中求四次多项式4次最小正实根的最快方法,python,algorithm,numpy,math,scipy,Python,Algorithm,Numpy,Math,Scipy,[我想要的]是找到四次函数ax^4+bx^3+cx^2+dx+e [现有方法] 我的方程用于碰撞预测,最大阶数是四次函数,如f(x)=ax^4+bx^3+cx^2+dx+e 和a、b、c、d、ecoef可以是正/负/零(实际浮点值)。所以我的函数f(x)可以是四次、三次或二次函数,具体取决于a、b、c、d、e输入系数 目前我使用numpy查找root,如下所示 进口numpy root_output=numpy.root([a,b,c,d,e]) 来自numpy模块的“根\u输出”可以是所有可能
- 在PC上多次运行numpy.root:1.2秒
- 在覆盆子Pi2上多次运行numpy.roots:17秒
- 二次函数只需要实正根(请注意被零除)
def SolvQuadratic(a、b、c): d=(b**2)-(4*a*c) 如果d<0: 返回[] 如果d>0: 平方根=math.sqrt(d) t1=(-b+平方根d)/(2*a) t2=(-b-平方根d)/(2*a) 如果t1>0: 如果t2>0: 如果t1
0: 返回[t2] 其他: 返回[] 其他: t=-b/(2*a) 如果t>0: 返回[t] 返回[] - 四次函数对于四次函数,您可以使用纯python/numba版本作为@B.M.的以下答案。我还从@B.M的代码中添加了另一个cython版本。您可以使用下面的代码作为.pyx文件,然后将其编译为比纯python快2倍左右(请注意舍入问题)
导入cmath “complex.h”中的cdef外部: 双复形cexp(双复形) cdef双复数J=cexp(2j*cmath.pi/3) cdef双复数Jc=1/J cdef卡达诺(双a、双b、双c、双d): cdef双z0 cdef双a2,b2 双p,q,D 双复形 双复数u,v,w cdef双w0,w1,w2 cdef双络合物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=(-q-r)/2)**0.33333 v=(-q+r)/2)**0.33333 w=u*v w0=防抱死制动系统(w+p/3) w1=abs(w*J+p/3) w2=abs(w*Jc+p/3)
如果w0则不使用循环的numpy解决方案为:p=array([a,b,c,d,e]) r=roots(p) r[(r.imag==0) & (r.real>=0) ].real.min()
方法将较慢,除非您不需要精度:scipy.optimize
然后你必须找到最小的 编辑 对于2x因子,请自己执行roots所做的操作:In [586]: %timeit r=roots(p);r[(r.imag==0) & (r.real>=0) ].real.min() 1000 loops, best of 3: 334 µs per loop In [587]: %timeit newton(poly1d(p),10,tol=1e-8) 1000 loops, best of 3: 555 µs per loop In [588]: %timeit newton(poly1d(p),10,tol=1) 10000 loops, best of 3: 177 µs per loop
另一个答案是: 使用分析方法(,)执行此操作,并使用即时编译()加快代码速度: 让我们先看看改进:In [638]: b=zeros((4,4),float);b[1:,:-1]=eye(3) In [639]: c=b.copy();c[0]=-(p/p[0])[1:];eig(c)[0] Out[639]: array([-7.40849430+0.j , 5.77969794+0.j , -0.18560182+3.48995646j, -0.18560182-3.48995646j]) In [640]: roots(p) Out[640]: array([-7.40849430+0.j , 5.77969794+0.j , -0.18560182+3.48995646j, -0.18560182-3.48995646j]) In [641]: %timeit roots(p) 1000 loops, best of 3: 365 µs per loop In [642]: %timeit c=b.copy();c[0]=-(p/p[0])[1:];eig(c) 10000 loops, best of 3: 181 µs per loop
然后是丑陋的代码: 对于订单4:In [2]: P=poly1d([1,2,3,4],True) In [3]: roots(P) Out[3]: array([ 4., 3., 2., 1.]) In [4]: %timeit roots(P) 1000 loops, best of 3: 465 µs per loop In [5]: ferrari(*P.coeffs) Out[5]: ((1+0j), (2-0j), (3+0j), (4-0j)) In [5]: %timeit ferrari(*P.coeffs) #pure python without jit 10000 loops, best of 3: 116 µs per loop In [6]: %timeit ferrari(*P.coeffs) # with numba.jit 100000 loops, best of 3: 13 µs per loop
@jit(nopython=True) def ferrari(a,b,c,d,e): "resolution of P=ax^4+bx^3+cx^2+dx+e=0" "CN all coeffs real." "First shift : x= z-b/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 X so P2=AX^3+BX^2+C^X+D=0" A=8 B=-4*p C=-8*r D=4*r*p-q*q y0,y1,y2=cardan(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.real)**.5 if a0==0 : b0=y0**2-r else : b0=-q/2/a0 r0,r1=roots2(1,a0,y0+b0) r2,r3=roots2(1,-a0,y0-b0) return (r0-z0,r1-z0,r2-z0,r3-z0)
可能还需要进一步测试,但效率很高。如果多项式系数提前已知,可以通过将计算矢量化为根来加快速度(给定Numpy>=1.10左右):
它可能比不上解析解+Numba,但允许更高的阶数。你能澄清一下你的意思吗:“和许多numpy.roots/一个循环对我的项目来说太慢了”?不太清楚,我的意思是numpy.roots太慢了,我需要知道找到根的更快方法。因为numpy.root可以找到所有可能的实/复根。但我只想知道唯一一个正的实根输出。因此,我认为可能有人知道如何比numpy.root更快地找到它。不幸的是,我无法帮助您使用scipy/numpy,但我可以建议您缓存您的结果,即确保您永远不会使用相同的(a、b、c、d、e)集执行numpy.root()。不过我不知道——也许你已经在这么做了……除此之外,你可能想在这里问:好的……谢谢@MaxU。(a,b,c,d,e)总是变化的。它有很低的可能性是相同的值。但我会把你的条件添加到我的主题中,让另一个人知道我问题的更多范围。我对这个社区很陌生。我如何将此主题标记到[link]math.stackexchange.com,或者我应该创建一个新主题到[link]math.stackexchange.com?谢谢,谢谢。但是cpu密集型在np.roots函数中。在我的PC上运行µs时间是可以接受的。在我的电脑上执行numpy.roots的次数很多,大约需要1.2秒。但在我的Raspberry Pi2上运行相同的代码需要17秒(慢14倍)。所以我找到了只提取实正根的解决方案,它比numpy.roots快。我在In [638]: b=zeros((4,4),float);b[1:,:-1]=eye(3) In [639]: c=b.copy();c[0]=-(p/p[0])[1:];eig(c)[0] Out[639]: array([-7.40849430+0.j , 5.77969794+0.j , -0.18560182+3.48995646j, -0.18560182-3.48995646j]) In [640]: roots(p) Out[640]: array([-7.40849430+0.j , 5.77969794+0.j , -0.18560182+3.48995646j, -0.18560182-3.48995646j]) In [641]: %timeit roots(p) 1000 loops, best of 3: 365 µs per loop In [642]: %timeit c=b.copy();c[0]=-(p/p[0])[1:];eig(c) 10000 loops, best of 3: 181 µs per loop
In [2]: P=poly1d([1,2,3,4],True) In [3]: roots(P) Out[3]: array([ 4., 3., 2., 1.]) In [4]: %timeit roots(P) 1000 loops, best of 3: 465 µs per loop In [5]: ferrari(*P.coeffs) Out[5]: ((1+0j), (2-0j), (3+0j), (4-0j)) In [5]: %timeit ferrari(*P.coeffs) #pure python without jit 10000 loops, best of 3: 116 µs per loop In [6]: %timeit ferrari(*P.coeffs) # with numba.jit 100000 loops, best of 3: 13 µs per loop
@jit(nopython=True) def ferrari(a,b,c,d,e): "resolution of P=ax^4+bx^3+cx^2+dx+e=0" "CN all coeffs real." "First shift : x= z-b/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 X so P2=AX^3+BX^2+C^X+D=0" A=8 B=-4*p C=-8*r D=4*r*p-q*q y0,y1,y2=cardan(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.real)**.5 if a0==0 : b0=y0**2-r else : b0=-q/2/a0 r0,r1=roots2(1,a0,y0+b0) r2,r3=roots2(1,-a0,y0-b0) return (r0-z0,r1-z0,r2-z0,r3-z0)
J=exp(2j*pi/3) Jc=1/J @jit(nopython=True) def cardan(a,b,c,d): u=empty(2,complex128) 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=sqrt(-D/27+0j) u=((-q-r)/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 roots2(a,b,c): bp=b/2 delta=bp*bp-a*c u1=(-bp-delta**.5)/a u2=-u1-b/a return u1,u2
import numpy as np def roots_vec(p): p = np.atleast_1d(p) n = p.shape[-1] A = np.zeros(p.shape[:1] + (n-1, n-1), float) A[...,1:,:-1] = np.eye(n-2) A[...,0,:] = -p[...,1:]/p[...,None,0] return np.linalg.eigvals(A) def roots_loop(p): r = [] for pp in p: r.append(np.roots(pp)) return r p = np.random.rand(2000, 4) # 2000 polynomials of 4th order assert np.allclose(roots_vec(p), roots_loop(p)) In [35]: %timeit roots_vec(p) 100 loops, best of 3: 4.49 ms per loop In [36]: %timeit roots_loop(p) 10 loops, best of 3: 81.9 ms per loop