![](/img/trans.png)
[英]Why calculating small fibonacci sequence with Python interpreter faster than PyPy
[英]Why is my code slower in pypy than the default python interpreter?
給定多個參與者n
,我需要找到H
,所有元組的列表,其中每個元組是聯盟的組合(參與者的角色,例如(1,2,3)是參與者1、2和3的聯盟。 ((1,2,3),(4,5),(6,))是遵守此規則的聯盟的組合-也是元組):每個玩家僅出現一次且僅出現一次(即僅出現在一個聯盟中) 。 PS聯盟的每種組合在代碼中稱為布局。
在開始時,我編寫了一個代碼段,在其中計算了所有聯盟的所有組合,並針對每個組合檢查了規則。 問題在於,對於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.
我計算排列(燙發)。 最后,對於每個燙發和每個梳子,我計算出不同的可能布局。 我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這么慢? 我應該改變什么?
首先,我想指出的是,您正在研究貝爾數 ,當您生成完所有子集后,這可能會減輕您的下一部分工作。 例如,很容易知道每個Bell集合的大小。 OEIS已經具有貝爾編號序列 。
我手工編寫了循環以生成Bell集。 這是我的代碼:
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)
。
使用標准timeit
模塊的CPython 2.7.11基准:
$ 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.
這是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代碼,因此這些功能變慢也就不足為奇了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.