[英]numpy calculate polynom efficiently
我正在尝试使用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
我错过了什么?
numpy中还有另一种评估多项式的方法吗?
像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;
或者(甚至更糟!),
p=c[0]+c[1]*x+c[2]*pow(x,2.0)+c[3]*pow(x,3.0)+c[4]*pow(x,4.0);
来(计算机)革命,所有被认定犯有此类犯罪行为的人将被即决处决,他们的计划将不会! 然而,这是一个品味问题,是否写作
p = c[0]+x*(c[1]+x*(c[2]+x*(c[3]+x*c[4])));
要么
p = (((c[4]*x+c[3])*x+c[2])*x+c[1])*x+c[0];
因此,如果您真的担心性能,那么您希望尝试这一点,对于更高次多项式,差异将是巨大的:
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
如果你想坚持numpy,有一个更新的多项式poly1d
我的系统上的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
那么,看看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
一方面,避免电源操作应该是速度方面的优势,另一方面,python级别的循环几乎搞砸了。
这是一个替代的numpy-ish实现:
POW = np.arange(100)[::-1]
def g(m, x):
return np.dot(m, x ** POW[m.size : ])
为了提高速度,我避免在每次通话时重新创建电源阵列。 另外,为了公平地对numpy进行基准测试时,你应该从numpy数组开始,而不是列表,以避免在每次调用时将列表转换为numpy的惩罚。
因此,当添加m = np.array(m)
,我的g
仅比你的f
慢约50%。
尽管你发布的例子比较慢,但是为了评估标量x
上的低次多项式,你真的不能比一个明确的实现(比如你的f
)快得多(当然你可以 ,但可能没那么多没有求助于编写低级代码)。 然而,对于更高的度数(你必须用某种循环替换你的explict表达式),numpy方法(例如g
)随着度数的增加会更快,并且对于矢量化评估也是如此,即当x
是向量时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.