【发布时间】:2016-08-03 01:45:58
【问题描述】:
有序列表缩减
我需要减少一些列表,其中取决于元素类型,二元运算的速度和实现会有所不同,即通过首先减少一些具有特定功能的对可以大幅降低速度。
例如foo(a[0], bar(a[1], a[2]))
可能比bar(foo(a[0], a[1]), a[2]) 慢很多,但在这种情况下给出相同的结果。
我已经有代码以元组列表(pair_index, binary_function) 的形式生成最佳排序。我正在努力实现一个有效的函数来执行归约,理想情况下,它返回一个新的部分函数,然后可以在相同类型排序但不同值的列表上重复使用。
简单而缓慢(?)的解决方案
这是我的简单解决方案,涉及 for 循环、删除元素和关闭 (pair_index, binary_function) 列表以返回“预计算”函数。
def ordered_reduce(a, pair_indexes, binary_functions, precompute=False):
"""
a: list to reduce, length n
pair_indexes: order of pairs to reduce, length (n-1)
binary_functions: functions to use for each reduction, length (n-1)
"""
def ord_red_func(x):
y = list(x) # copy so as not to eat up
for p, f in zip(pair_indexes, binary_functions):
b = f(y[p], y[p+1])
# Replace pair
del y[p]
y[p] = b
return y[0]
return ord_red_func if precompute else ord_red_func(a)
>>> foos = (lambda a, b: a - b, lambda a, b: a + b, lambda a, b: a * b)
>>> ordered_reduce([1, 2, 3, 4], (2, 1, 0), foos)
1
>>> 1 * (2 + (3-4))
1
以及预计算的工作原理:
>>> foo = ordered_reduce(None, (0, 1, 0), foos)
>>> foo([1, 2, 3, 4])
-7
>>> (1 - 2) * (3 + 4)
-7
但是,它涉及复制整个列表并且也(因此?)很慢。有没有更好/标准的方法来做到这一点?
(编辑:)一些时间:
from operators import add
from functools import reduce
from itertools import repeat
from random import random
r = 100000
xs = [random() for _ in range(r)]
# slightly trivial choices of pairs and functions, to replicate reduce
ps = [0]*(r-1)
fs = repeat(add)
foo = ordered_reduce(None, ps, fs, precompute=True)
>>> %timeit reduce(add, xs)
100 loops, best of 3: 3.59 ms per loop
>>> %timeit foo(xs)
1 loop, best of 3: 1.44 s per loop
这是一种最坏的情况,并且由于reduce不采用可迭代的函数,但有一点作弊(但没有顺序)仍然非常快:
def multi_reduce(fs, xs):
xs = iter(xs)
x = next(xs)
for f, nx in zip(fs, xs):
x = f(x, nx)
return x
>>> %timeit multi_reduce(fs, xs)
100 loops, best of 3: 8.71 ms per loop
(EDIT2):为了好玩,一个大规模作弊的“编译”版本的性能,它给出了发生的总开销的一些概念。
from numba import jit
@jit(nopython=True)
def numba_sum(xs):
y = 0
for x in xs:
y += x
return y
>>> %timeit numba_sum(xs)
1000 loops, best of 3: 1.46 ms per loop
【问题讨论】:
-
你能举一些例子,其中
ordered_reduce比直接计算慢(如foo([1, 2, 3, 4])vs(1 - 2) * (3 + 4))? -
添加了一些计时! @ptrj
-
只有在发布我的答案后,我才在您的
ord_red_func上运行分析器。正如我所料,del y[p]是罪魁祸首。该程序在示例中该行中几乎 90% 的时间用于添加数字,在我的示例中大约 50% 的时间用于矩阵。所以,我认为,任何摆脱这个del的解决方案都可以。
标签: python performance list reduce partial-application