Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python中求四次多项式4次最小正实根的最快方法_Python_Algorithm_Numpy_Math_Scipy - Fatal编程技术网

python中求四次多项式4次最小正实根的最快方法

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输出”可以是所有可能

[我想要的]是找到四次函数ax^4+bx^3+cx^2+dx+e

[现有方法] 我的方程用于碰撞预测,最大阶数是四次函数,如f(x)=ax^4+bx^3+cx^2+dx+ea、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上运行代码。下面是处理时间的示例

  • 在PC上多次运行numpy.root:1.2秒
  • 在覆盆子Pi2上多次运行numpy.roots:17秒
您能指导我如何在最快的解决方案中找到最小的正实根吗?使用scipy.optimize或实现一些算法来加速查找根或任何来自您的建议都将非常好

多谢各位

[解决方案]

  • 二次函数只需要实正根(请注意被零除)

    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:
    如果t10:
    返回[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
    方法将较慢,除非您不需要精度:

    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
    
    然后你必须找到最小的

    编辑

    对于2x因子,请自己执行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
    
    然后是丑陋的代码:

    对于订单4:

    @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