鉴于一些玩家n
,我需要找到H
,所有元组的列表,其中每个元组是联盟的组合(玩家,例如(1,2,3))球员1,2和3的联盟。((1,2,3),(4,5),(6,))是一个尊重这个规则的联盟 - 也是元组的组合:每个球员出现只有一次(即只有一个联盟)。
附:联盟的每个组合在代码中称为布局。
一开始我写了一个片段,其中我计算了所有联盟的所有组合,并且对于每个组合我检查了规则。问题是,对于5-6名球员来说,联盟组合的数量已经很大,以至于我的计算机变得很糟糕。 为了避免计算的大部分(所有可能的组合,循环和ifs),我写了以下内容(我测试了它,它等同于前面的代码片段):
from itertools import combinations, combinations_with_replacement, product, permutations
players = range(1,n+1)
coalitions = [[coal for coal in list(combinations(players,length))] for length in players]
H = [tuple(coalitions[0]),(coalitions[-1][0],)]
combs = [comb for length in xrange(2,n) for comb in combinations_with_replacement(players,length) if sum(comb) == n]
perms = list(permutations(players))
layouts = set(frozenset(frozenset(perm[i:i+x]) for (i,x) in zip([0]+[sum(comb[:y]) for y in xrange(1,len(comb))],comb)) for comb in combs for perm in perms)
H.extend(tuple(tuple(tuple(coal) for coal in layout) for layout in layouts))
print H
解释:说n = 3
首先,我创建所有可能的联盟:
coalitions = [[(1,),(2,),(3,)],[(1,2),(1,3),(2,3)],[(1,2,3)]]
然后我用明显的组合初始化H:他自己的联盟中的每个球员和最大联盟中的每个球员。
H = [((1,),(2,),(3,)),((1,2,3),)]
然后我计算所有可能的布局形式:
combs = [(1,2)] #(1,2) represents a layout in which there is
#one 1-player coalition and one 2-player coalition.
我计算排列(perms)。
最后,对于每个烫发和每个梳子,我计算不同的可能布局。我set
结果(layouts
)以删除重复项并添加到H.
H = [((1,),(2,),(3,)),((1,2,3),),((1,2),(3,)),((1,3),(2,)),((2,3),(1,))]
以下是比较:
python script.py
pypy script.py
为什么pypy会变慢?我应该改变什么?
答案 0 :(得分:3)
首先,我想指出你正在研究Bell numbers,这可能会在你完成生成所有子集之后简化你工作的下一部分。例如,很容易知道每个Bell组的大小; OEIS已经the sequence of Bell numbers。
我亲手编写循环以生成贝尔集;这是我的代码:
cache = {0: (), 1: ((set([1]),),)}
def bell(x):
# Change these lines to alter memoization.
if x in cache:
return cache[x]
previous = bell(x - 1)
new = []
for sets in previous:
r = []
for mark in range(len(sets)):
l = [s | set([x]) if i == mark else s for i, s in enumerate(sets)]
r.append(tuple(l))
new.extend(r)
new.append(sets + (set([x]),))
cache[x] = tuple(new)
return new
为了实际目的,我在这里包含了一些备忘录。但是,通过注释掉一些代码并编写其他代码,您可以获得以下未记忆的版本,我将其用于基准测试:
def bell(x):
if x == 0:
return ()
if x == 1:
return ((set([1]),),)
previous = bell(x - 1)
new = []
for sets in previous:
r = []
for mark in range(len(sets)):
l = [s | set([x]) if i == mark else s for i, s in enumerate(sets)]
r.append(tuple(l))
new.extend(r)
new.append(sets + (set([x]),))
cache[x] = tuple(new)
return new
我的数据是基于几年前的Thinkpad,我完成了大部分工作。大多数较小的案例都太快而无法可靠地测量(前几个案例的每个试验甚至没有一毫秒),因此我的基准测试正在测试bell(9)
到bell(11)
。
CPython 2.7.11的基准测试,使用标准timeit
模块:
$ python -mtimeit -s 'from derp import bell' 'bell(9)'
10 loops, best of 3: 31.5 msec per loop
$ python -mtimeit -s 'from derp import bell' 'bell(10)'
10 loops, best of 3: 176 msec per loop
$ python -mtimeit -s 'from derp import bell' 'bell(11)'
10 loops, best of 3: 1.07 sec per loop
在PyPy 4.0.1上,也使用timeit
:
$ pypy -mtimeit -s 'from derp import bell' 'bell(9)'
100 loops, best of 3: 14.3 msec per loop
$ pypy -mtimeit -s 'from derp import bell' 'bell(10)'
10 loops, best of 3: 90.8 msec per loop
$ pypy -mtimeit -s 'from derp import bell' 'bell(11)'
10 loops, best of 3: 675 msec per loop
所以,我得出的结论是itertools
当你试图在它的预期习语之外使用它时不是很快。贝尔数字是组合有趣的,但它们自然不会出现在我能找到的itertools
小部件的任何简单组合中。
响应您对如何加快速度的原始查询:只需打开代码即可。希望这有帮助!
~C。
答案 1 :(得分:1)
这是itertools.product
上的Pypy问题。
https://bitbucket.org/pypy/pypy/issues/1677/itertoolsproduct-slower-than-nested-fors
请注意,我们的目标是确保itertools的速度不会大于 简单的Python,但我们并不真正关心如何快速(或 更快)作为普通的Python。只要它没有大幅减慢,它就没问题了。 (在 至少我不同意你关于a)或b)是否更容易阅读: - )
如果不详细研究您的代码,它似乎会大量使用itertools
组合,排列和产品功能。在常规CPython中,这些是用编译的C代码编写的,目的是使它们快速。 Pypy没有实现C代码,因此这些函数速度较慢并不令人惊讶。