为什么Python的numpy.polyval()这么慢?

时间:2014-06-10 20:47:58

标签: python numpy

更新脚本中有错误。

我正在研究Julia&amp ;; Mandelbrot设置和Newton分形 - 为此我需要在复平面中计算很多的值。我可以使用我想要的任何类型的数学函数,但它足以用于多项式。

我需要计算函数/多项式的导数和值,所以我查看了numpy模块,找到了关于numpy.polyder()numpy.polyval()的信息。它看起来就像我需要的东西,但突然间我的脚本变得非常慢。

我试着想一些简单的测试来显示时间上的差异。为此我编写了以下脚本:

import numpy as np
import cmath
import time
from itertools import product

C   = 0.37 + 0.45j
pol = [1,0,0]

start_time = time.time()
for i in xrange(100000):
    C = np.polyval(pol, C)

print "Polyval: {}".format( time.time() - start_time )
print C

C   = 0.37 + 0.45j     # forgot to reassign the initial value of C
start_time = time.time()
for i in xrange(100000):
    C = C**2

print "Standard: {}".format( time.time() - start_time )
print C

基本上这个脚本计算多项式g(C)= C ** 2的很多值。时间结果是(程序的实际输出):

Polyval: 2.34903216362
0j
Standard: 0.0198249816895
0j

我可能没有以最好的方式设置此测试,这是第一次做这样的事情。但即使出现任何错误,运行我的其他脚本也会显示出很大的差异。

问题

有没有办法让它更快?我理解调用另一个函数是耗时的,但仍然。我是否应该重新考虑能够仅在一个地方改变多项式系数的优势,以及时间上的劣势?关于如何处理此类问题的任何其他建议?

2 个答案:

答案 0 :(得分:2)

本身不是答案,但我写了一个函数polyval_factory来生成一个mypolyval函数给定一系列系数,如pol。它以字符串形式生成快速表达式,如C*C + 1.0 * C *C*C + 2.0,然后将它们装入lambda函数中。基本上它使用字符串代替完整的符号代数库,如sympy。在下面的示例中定义并测试了此函数,其工作速度几乎与C*C

一样快
import numpy as np
import cmath
import time
from itertools import product

def polyval_factory(pol):
    """ 
    Generate a lambda function for evaluating a given polynomial

    pol : a list of coefficients with highest degree first

    Note: this function basically uses strings in lieu of a 
          fully symbolic algebra package like sympy
    """
    poly_string_list = []
    for i, coeff in enumerate(pol[::-1]):

        if np.abs(coeff) > 1e-10:
            if i > 1:
                poly_string_list.append( repr(coeff) + '*' + '*'.join(['x']*i))
            elif i == 1:
                poly_string_list.append(repr(coeff)+'*x')
            elif i ==0:
                poly_string_list.append(repr(coeff))

    lambda_str = 'lambda x :' + '+'.join(poly_string_list)

    print "The prepared lambda function is: \""+lambda_str + "\""

    return eval(lambda_str)

C   = 0.37 + 0.45j
pol = [1,0,0]

numiter  = 30000

start_time = time.time()
for i in xrange(numiter):
    C = np.polyval(pol, C)

print "Polyval: {}".format( time.time() - start_time )
print C

C   = 0.37 + 0.45j     # forgot to reassign the initial value of C
print ""
print "Generating lambda function..."
mypolyval = polyval_factory(pol) # generate the lambda function
print ""

start_time = time.time()
for i in xrange(numiter):
    C = mypolyval(C)
print "Polyval_factory: {}".format( time.time() - start_time )
print C

C   = 0.37 + 0.45j     # forgot to reassign the initial value of C
print ""
start_time = time.time()
for i in xrange(numiter):
    C = C**2
print "Standard: {}".format( time.time() - start_time )
print C

输出是:

Polyval: 0.738290071487
0j

Generating lambda function...
The prepared lambda function is: "lambda x :1*x*x"

Polyval_factory: 0.013610124588
0j

Standard: 0.00678110122681
0j

修改polyval_factory:现在正在运行mypolyval = polyval_factory([2.0,3.0,1.0])做适当的事情并打印出来:

    The prepared lambda function is: "lambda x :1.0+3.0*x+2.0*x*x"

答案 1 :(得分:2)

使用Horner方法时乘法顺序的奇怪性在速度上占约5倍。这在numpy 1.10中已修复,或者您可以使用已经修复的numpy.polynomial.polynomial.polyval。有可能进一步矢量化,例如在numpy.polynomial.polynomial.polyval版本中使用2d系数数组,但我不清楚你的要求是什么。另请注意,您可以直接将多项式迭代为p(p),但在某些时候我预计会导致数值问题。