numpy有效地计算多项式

mcl*_*fee 5 python performance numpy polynomials

我正在尝试使用numpy来评估多项式(3度).我发现通过更简单的python代码来实现它会更有效率.

import numpy as np
import timeit

m = [3,7,1,2]

f = lambda m,x: m[0]*x**3 + m[1]*x**2 + m[2]*x + m[3]
np_poly = np.poly1d(m)
np_polyval = lambda m,x: np.polyval(m,x)
np_pow = lambda m,x: np.power(x,[3,2,1,0]).dot(m)

print 'result={}, timeit={}'.format(f(m,12),timeit.Timer('f(m,12)', 'from __main__   import f,m').timeit(10000))
result=6206, timeit=0.0036780834198

print 'result={}, timeit={}'.format(np_poly(12),timeit.Timer('np_poly(12)', 'from __main__ import np_poly').timeit(10000))
result=6206, timeit=0.180546045303

print 'result={}, timeit={}'.format(np_polyval(m,12),timeit.Timer('np_polyval(m,12)', 'from __main__ import np_polyval,m').timeit(10000))
result=6206, timeit=0.227771043777

print 'result={}, timeit={}'.format(np_pow(m,12),timeit.Timer('np_pow(m,12)', 'from __main__ import np_pow,m').timeit(10000))
result=6206, timeit=0.168987989426
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

numpy中还有另一种评估多项式的​​方法吗?

Jai*_*ime 7

像23年前的事情,我从大学的图书馆查看了一份Press et al Numerical Recipes in C的副本.那本书中有很多很酷的东西,但这里有一段多年来一直困扰着我的文章,第173页:

我们假设你知道永远不会以这种方式评估多项式:

    p=c[0]+c[1]*x+c[2]*x*x+c[3]*x*x*x+c[4]*x*x*x*x;
Run Code Online (Sandbox Code Playgroud)

或者(甚至更糟!),

    p=c[0]+c[1]*x+c[2]*pow(x,2.0)+c[3]*pow(x,3.0)+c[4]*pow(x,4.0);
Run Code Online (Sandbox Code Playgroud)

来(计算机)革命,所有被认定犯有此类犯罪行为的人将被即决处决,他们的计划将不会!然而,这是一个品味问题,是否写作

    p = c[0]+x*(c[1]+x*(c[2]+x*(c[3]+x*c[4])));
Run Code Online (Sandbox Code Playgroud)

要么

    p = (((c[4]*x+c[3])*x+c[2])*x+c[1])*x+c[0];
Run Code Online (Sandbox Code Playgroud)

因此,如果您真的担心性能,那么您希望尝试这一点,对于更高次多项式,差异将是巨大的:

In [24]: fast_f = lambda m, x: m[3] + x*(m[1] + x*(m[2] + x*m[3]))

In [25]: %timeit f(m, 12)
1000000 loops, best of 3: 478 ns per loop

In [26]: %timeit fast_f(m, 12)
1000000 loops, best of 3: 374 ns per loop
Run Code Online (Sandbox Code Playgroud)

如果你想坚持numpy,有一个更新的多项式类比poly1d我的系统运行速度快2倍,但仍然比前面的循环慢得多:

In [27]: np_fast_poly = np.polynomial.polynomial.Polynomial(m[::-1])

In [28]: %timeit np_poly(12)
100000 loops, best of 3: 15.4 us per loop

In [29]: %timeit np_fast_poly(12)
100000 loops, best of 3: 8.01 us per loop
Run Code Online (Sandbox Code Playgroud)

  • +1这被称为[Horner的方法](https://en.wikipedia.org/wiki/Horner's_method)或[Horner形式](https://reference.wolfram.com/language/ref/HornerForm.html) ,并对我的一些代码进行了巨大的*改进. (2认同)

shx*_*hx2 1

好吧,看看它的实现polyval(这是当你评估 poly1d 时最终被调用的函数),实现者决定包含一个显式循环似乎很奇怪......来自 numpy 1.6.2 的来源:

def polyval(p, x):
    p = NX.asarray(p)
    if isinstance(x, poly1d):
        y = 0
    else:
        x = NX.asarray(x)
        y = NX.zeros_like(x)
    for i in range(len(p)):
        y = x * y + p[i]
    return y
Run Code Online (Sandbox Code Playgroud)

一方面,避免幂运算应该在速度方面是有利的,另一方面,Python 级循环几乎把事情搞砸了。

这是另一种 numpy-ish 实现:

POW = np.arange(100)[::-1]
def g(m, x):
    return np.dot(m, x ** POW[m.size : ])
Run Code Online (Sandbox Code Playgroud)

为了速度,我避免在每次调用时重新创建电源阵列。另外,为了公平起见,在对 numpy 进行基准测试时,您应该从 numpy 数组而不是列表开始,以避免每次调用时将列表转换为 numpy 的惩罚。

所以,当添加时m = np.array(m),我的g上面的运行速度只比你的慢 50% 左右f

尽管在您发布的示例上速度较慢,但​​为了评估标量上的低次多项式x,您确实不能比显式实现(例如您的f)做得更快(当然可以,但如果不诉诸于,可能不会快很多)编写较低级别的代码)。然而,对于更高的度数(您必须用某种循环替换显式表达式),g随着度数的增加,numpy 方法(例如 )将证明更快,并且对于矢量化评估(即何时x是矢量)来说也是如此。

  • -1 numpy 源代码以 Horner 形式进行评估,这可能不容易矢量化,但远远优于您编写的内容。(参见这个问题的另一个答案。) (2认同)