【问题标题】:Optimising a simple mathematical calculation over list优化列表的简单数学计算
【发布时间】:2012-09-03 23:30:35
【问题描述】:

我有一个正在计算的脚本:

def sumsquared(arr):
    sum = 0
    idx = 0
    len = arr.__len__()
    while idx < (len - 1):
            sum = sum + (arr[idx] * arr[idx]) + (arr[idx+1] * arr[idx+1])
            idx = idx + 2

    return sum

上述函数在一个循环中调用,该循环填充两个列表并调用此函数两次:第一次使用 len ~ 1024 个项目的列表,第二次使用 len ~ 44100 个项目。循环本身可以运行 100 到 100000 次,具体取决于输入。

对于小尺寸输入,基于cProfile 的分析会通知我:

ncalls  tottime  percall  cumtime  percall  filename:lineno(function)
---------------------------------------------------------------------
 2560   12.065   0.005    12.065    0.005    beat.py:8(sumsquared)

这大约是脚本总运行时间的 95%。有什么方法可以加快这个功能吗?

【问题讨论】:

  • 你应该可以使用len = len(arr),不需要调用.__len__()
  • @monkut 这不起作用,因为len 是函数中的一个局部变量,它将阻止访问内置的len()。然而,WeaklyTyped 应该能够做到length = len(arr)
  • 是的,或者只使用 len(arr) 本身而不是创建变量。
  • 在这种情况下,不是每次循环检查条件时都会调用 len(arr) 吗?

标签: python optimization


【解决方案1】:

你的功能很奇怪。它所做的只是计算元素的平方和,除了如果有奇数个元素它会丢弃最后一个元素。您出于某种原因一次添加两个,但这不会影响最终结果。

为了使其更快,您可以使用numpy 而不是编写自己的函数。

>>> x = np.array([1, 2, 3, 4, 5])
>>> sumsquared(x)
30
>>> (x[:2*(len(x)//2)]**2).sum()
30

通常,如果您有数千个数字的列表,则使用 numpy 数组可能会带来重大的性能提升。

【讨论】:

  • 与 numpy 数组相互转换的成本很高。通常只有在您一起进行大量操作时才有意义。
  • @gnibbler:是的,但这就是我在最后添加的原因。如果您有 44000 个数字的列表,您可能应该从一开始就使用 numpy 数组。
【解决方案2】:

这看起来像是 itertools module 的工作:

def sumsquared(arr):
    s = sum(itertools.imap(operator.mul, arr, arr))
    return s - arr[-1] ** 2 if len(arr) & 1 else s

使用 sumoperatoritertools 将消除几乎所有纯 python 开销。

此外,sum 已经过优化,可以在输入为整数、浮点数或两者的混合时以接近 C 的速度运行。它能够累积运行总计,而无需为每个中间小计创建纯 Python 对象。

感谢:Robert King 提出了必要时减去最后一个平方的想法。

另外一点,如果您有兴趣获得高精度(即尽量减少精度损失),请考虑使用 math.fsum 而不是 sum

【讨论】:

  • 也许是这样,但你可以更具体一点。
  • pairwise 返回(s0,s1),(s1,s2) 问题要求(s0,s1),(s2,s3)
【解决方案3】:

这是我能找到的最快的

from itertools import imap
from operator import mul
def sumsquared(arr):
    return sum(imap(mul, arr, arr))

【讨论】:

    【解决方案4】:
    sum([i**2 for i in arr]) - (arr[-1]**2 if len(arr) % 2 else 0)
    

    【讨论】:

    • 你真的应该改用sum(i**2 for i in arr)。您的方法将计算整个列表,然后遍历该列表以对元素求和。我建议使用生成器理解而不是列表理解。在我的测试中,生成器理解将运行时间减少到 OP 算法的近 50%
    • guido 今天说:“我在使用生成器[表达式]时是-0,除非你知道你有大量的值需要迭代。具体的列表[理解]通常比基因expr 快直到内存分配变得至关重要。” - plus.google.com/s/guido
    猜你喜欢
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多