【问题标题】:numpy calculate polynom efficientlynumpy 有效地计算多项式
【发布时间】:2026-01-24 03:15:02
【问题描述】:

我正在尝试使用 numpy. 我发现用更简单的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 中是否有另一种方法来评估多项式?

【问题讨论】:

    标签: python numpy performance polynomials


    【解决方案1】:

    大约在 23 年前,我从大学图书馆中查阅了 Press et al Numerical Recipes in C 的副本。那本书里有很多很酷的东西,但有一段话多年来一直困扰着我,page 173 here

    我们假设你知道的足够多,永远不会计算多项式 this 方式:

        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 快 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
    

    【讨论】:

    • +1 这称为Horner's methodHorner form,对我的一些代码进行了巨大的改进。
    • 应该强调的是,这不仅仅是关于速度;在很多情况下,霍纳形式也提高了结果的准确性。特别是,一个简单地写的多项式可能会涉及很多取消,如果项的总和远小于项的绝对值的总和,则会导致数值错误。在霍纳形式中,不会发生这种取消。
    • @Mike 还有更好的方法。 “对于一大类多项式,多项式评估的标准方法霍纳方法可能非常不准确。这里给出的替代方法平均比霍纳方法准确 100 到 1000 倍。” - 多项式的准确评估,Sutin 2007
    【解决方案2】:

    好吧,看看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)(当然,您 可以,但如果不求助于编写较低级别的代码,可能不会太多)。但是,对于更高的度数(您必须用某种循环替换显式表达式),numpy 方法(例如g)会随着度数的增加而更快,并且对于矢量化评估也是如此,即当@987654330 @ 是一个向量。

    【讨论】:

    • “实现者决定包含一个显式循环似乎很奇怪”如果您尝试在一个块中完成所有操作,可能会导致内存错误?
    • -1 numpy 源正在以霍纳形式进行评估,这可能不容易矢量化,但远远优于您编写的。 (请参阅此问题的其他答案。)