Python递归生成器性能

时间:2013-05-24 09:21:14

标签: python performance recursion generator

在python中,当将纯递归函数更改为递归生成器(不是普通生成器)时,性能似乎会降低。

例如,下面是两个函数之间的性能比较,它们查找列表的所有组合:

from datetime import datetime as dt

def rec_subsets(ms, i=0, s=[]):
    if i == len(ms):
        # do something with s
        return
    rec_subsets(ms, i+1, s)
    rec_subsets(ms, i+1, s + [ms[i]])

def gen_subsets(ms, i=0, s=[]):
    if i == len(ms):
        yield s
        return
    for a in gen_subsets(ms, i+1, s): yield a
    for a in gen_subsets(ms, i+1, s + [ms[i]]): yield a

t1 = dt.now()
rec_subsets(range(20))
t2 = dt.now()
print t2 - t1

t1 = dt.now()
for _ in gen_subsets(range(20)): pass
t2 = dt.now()
print t2 - t1

使用以下输出:

0:00:01.027000  # rec_subsets
0:00:02.860000  # gen_subsets

人们自然希望 gen_subsets rec_subsets 一样快,但事实并非如此,它要慢得多。

这是正常还是我错过了什么?

1 个答案:

答案 0 :(得分:3)

rec_subsets()仍然更快(对于range(20)),即使result.append(s)已添加到# do something with s以及gen_subsets()和{{1}的结果被消耗了。

可以通过PEP 380 (yield from syntax support)的以下引用来解释:

  

使用专门的语法为优化提供了可能性   当有一长串发电机时。这样的链可能会出现   例如,递归遍历树结构时。开销   传递rec_subsets()次呼叫并在链中产生值   在最糟糕的情况下,可能导致应该是 O(n)操作   case, O(n ** 2)

您可以使用itertools.combinations()生成一个powerset:

__next__()

我机器上from itertools import combinations def subsets_comb(lst): return (comb for r in range(len(lst)+1) for comb in combinations(lst, r)) 的速度更快:

range(20)

要重现结果run time-subsets.py