Python 优化极慢的置换搜索循环(不能使用itertools)。有什么建议吗?
这是一个游戏,你有12张牌,你选择你,直到你从同一组中选择3张。我试图找出选择每组的概率。我创建的脚本可以工作,但速度非常慢。我的同事在R中创建了一个类似的脚本,没有函数,他的脚本花费的时间是我的脚本的100分之一。我只是想弄清楚原因。任何想法都将不胜感激Python 优化极慢的置换搜索循环(不能使用itertools)。有什么建议吗?,python,optimization,permutation,itertools,pypy,Python,Optimization,Permutation,Itertools,Pypy,这是一个游戏,你有12张牌,你选择你,直到你从同一组中选择3张。我试图找出选择每组的概率。我创建的脚本可以工作,但速度非常慢。我的同事在R中创建了一个类似的脚本,没有函数,他的脚本花费的时间是我的脚本的100分之一。我只是想弄清楚原因。任何想法都将不胜感激 from collections import Counter import pandas as pd from datetime import datetime weight = pd.read_excel('V01Weights.xls
from collections import Counter
import pandas as pd
from datetime import datetime
weight = pd.read_excel('V01Weights.xlsx')
重量如下所示:
Symb Weight
Grand 170000
Grand 170000
Grand 105
Major 170000
Major 170000
Major 215
Minor 150000
Minor 150000
Minor 12000
Bonus 105000
Bonus 105000
Bonus 105000
Max Picks表示不同“卡”的总数。Total Picks表示用户选择的最大数量。这是因为在8次选择之后,你保证每种类型有2次,所以在第9次选择时,你保证有3次匹配
TotalPicks = 9
MaxPicks = 12
这应该被命名为PickedProbabilities
Picks = {0:0,1:0,2:0,3:0}
这是我的timeit类的简单版本,因为我不喜欢timeit类
def Time_It(function):
start =datetime.now()
x = function()
finish = datetime.now()
TotalTime = finish - start
Minutes = int(TotalTime.seconds/60)
Seconds = TotalTime.seconds % 60
print('It took ' + str(Minutes) + ' minutes and ' + str(Seconds) + ' seconds')
return(x)
给定x(我的选择顺序),我找到概率。这些镐没有更换
def Get_Prob(x,weight):
prob = 1
weights = weight.iloc[:,1]
for index in x:
num = weights[index]
denom = sum(weights)
prob *= num/denom
weights.drop(index, inplace = True)
# print(weights)
return(prob)
这用于确定我的循环中是否存在重复项,因为这是不允许的
def Is_Allowed(x):
return(len(x) == len(set(x)))
这决定了目前为止所有的牌中是否都有赢牌
def Is_Win(x):
global Picks
WinTypes = [[0,1,2],[3,4,5],[6,7,8],[9,10,11]]
IsWin = False
for index,item in enumerate(WinTypes):
# print(index)
if set(item).issubset(set(x)):
IsWin = True
Picks[index] += Get_Prob(x,weight)
# print(Picks[index])
print(sum(Picks.values()))
break
return(IsWin)
这是我的主要功能,可以循环使用所有卡。我试图使用递归来实现这一点,但最终还是放弃了。我无法使用itertools创建所有置换,因为例如[0,1,2,3,4]将由itertools创建,但这是不可能的,因为一旦获得3个匹配,游戏结束
def Cycle():
for a in range(MaxPicks):
x = [a]
for b in range(MaxPicks):
x = [a,b]
if Is_Allowed(x):
for c in range(MaxPicks):
x = [a,b,c]
if Is_Allowed(x):
if Is_Win(x):
# print(x)
continue
for d in range(MaxPicks):
x = [a,b,c,d]
if Is_Allowed(x):
if Is_Win(x):
# print(x)
continue
for e in range(MaxPicks):
x = [a,b,c,d,e]
if Is_Allowed(x):
if Is_Win(x):
continue
for f in range(MaxPicks):
x = [a,b,c,d,e,f]
if Is_Allowed(x):
if Is_Win(x):
continue
for g in range(MaxPicks):
x = [a,b,c,d,e,f,g]
if Is_Allowed(x):
if Is_Win(x):
continue
for h in range(MaxPicks):
x = [a,b,c,d,e,f,g,h]
if Is_Allowed(x):
if Is_Win(x):
continue
for i in range(MaxPicks):
if Is_Allowed(x):
if Is_Win(x):
continue
调用主函数
x = Time_It(Cycle)
print(x)
将概率写入文本文件
with open('result.txt','w') as file:
# file.write(pickle.dumps(x))
for item in x:
file.write(str(item) + ',' + str(x[item]) + '\n')
我的同事在R中创建了一个类似的脚本,没有函数,他的脚本花费的时间是我的脚本的100分之一
两个简单的优化:
1) 实际上,像这样的函数调用是允许的()
,因为Python有很多函数调用开销(例如创建新的stackframe和参数元组)
2) 在中运行代码,该代码非常擅长优化像这样的函数。编辑:我误解了原始问题,这里提供的解决方案针对以下问题: 给4组3张不同的牌,每张牌的分数不同,只要我们没有从同一组中选3张牌,我们就选牌。游戏结束时的预期分数(所选牌的分数之和)是多少 我不考虑这个问题的解决方案,因为经过这么多年的概率论研究,我很高兴能解决这个问题,我就是不能删除它:) 有关原始问题的处理,请参见我的其他答案
提高性能有两种可能:使代码更快(在开始之前,应该分析程序的哪些部分应该优化,否则时间就花在优化不重要的事情上)或改进算法。我提议做第二件事 好的,这个问题似乎比第一个站点更复杂。让我们从一些观察开始
您需要知道的是游戏结束时所选牌的预期数量:
import math
def binom(n,k):
return math.factorial(n)//math.factorial(k)//math.factorial(n-k)
#expected number of cards:
n=12 #there are 12 cards
probs=[0]*n
for minor in xrange(3):
for major in xrange(3):
for bonus in xrange(3):
i = 3 + minor +major +bonus
P_urn = binom(3,2)*binom(3,minor)*binom(3,major)*binom(3,bonus)/float(binom(n, n-i+1))
P_right_last_card = 1.0/(n-i+1)
probs[i]+=4*P_urn*P_right_last_card #factor 4 from symmetry
print "Expected number of cards:", sum((prob*card_cnt for card_cnt, prob in enumerate(probs)))
如果Pi
是在游戏过程中某个地方拾取卡i
的概率,那么我们将寻找得分E(得分)=P1*W1+P2*W2+…Pn*Wn
的预期值。然而,如果我们看一组的卡片,我们可以说明,由于对称性,该组卡片的概率是相同的,例如,在您的案例中,P1=P2=P3=:Pgrand
。因此,可以计算出我们的期望值:
E(Score)=3*Pgrand*(W1+W2+W3)/3+...+3*Pbonus*(W10+W11+W12)/3
我们称之为averageWgrand:=(W1+W2+W3)/3
,并注意到E(#grand)=3*Pgrand
——游戏结束时挑选的大牌的预期数量。由此,我们的公式变成:
E(Score)=E(#grand)*averageWgrand+...+E(#bonus)*averageWbonus
在你的例子中,我们可以更进一步:每组的牌数相等,因此由于对称性,我们可以声称:E(#grand)=E(#minor)=E(#major)=E(#grand)=:(E#group)
。为了简单起见,在下面我们只考虑这种特殊情况(但概略的解决方案也可以扩展到一般情况)。这导致以下简化:
E(Score)=4*E(#group)(averageWgrand+...+averageWbonus)/4
我们将averageW:=(averageWgrand+…+averageWbonus)/4
称为E(#卡片)=4*E(#grand)
是游戏结束时所选卡片的预期数量
因此,E(Score)=E(#卡片)*averageW
,因此我们的任务简化为在游戏结束时计算卡片数量的预期值:
E(#cards)=P(1)*1+P(2)*2+...P(n)*n
其中p(i)
表示游戏以确切的i
牌结束的概率。概率P(1)
,P(2)
和P(k)
,k>9
很容易看到-它们是0
计算以
i
拾取的卡结束游戏的概率-p(i)
:
import math
def binom(n,k):
return math.factorial(n)//math.factorial(k)//math.factorial(n-k)
#expected number of cards:
n=12 #there are 12 cards
probs=[0]*n
for minor in xrange(3):
for major in xrange(3):
for bonus in xrange(3):
i = 3 + minor +major +bonus
P_urn = binom(3,2)*binom(3,minor)*binom(3,major)*binom(3,bonus)/float(binom(n, n-i+1))
P_right_last_card = 1.0/(n-i+1)
probs[i]+=4*P_urn*P_right_last_card #factor 4 from symmetry
print "Expected number of cards:", sum((prob*card_cnt for card_cnt, prob in enumerate(probs)))
让我们玩一个稍有不同的游戏:我们准确地选择i
牌,并在以下情况下获胜:
full\u组
full\u组
p(i)
。再一次,我们可以使用对称性,因为所有的群都是相等的(P(win,full=grand)
表示我们所做的和full\u群=grand
)的概率:
p(赢,大)
是以下概率:
i-1
卡后,拾取的大卡数为2,即“grand=2”和i-1
卡后,每组拾取的卡数少于3张,并且1/(n)-
P(win, grand) = 1/(n-i+1)*sum P(#grand=2, #minor=x, #major=y, #bonus=z)
where x<=2, y<=2, z<=2 and 2+x+y+z=i-1
import math
def binom(n,k):
return math.factorial(n)//math.factorial(k)//math.factorial(n-k)
#expected number of cards:
n=12 #there are 12 cards
probs=[0]*n
for minor in xrange(3):
for major in xrange(3):
for bonus in xrange(3):
i = 3 + minor +major +bonus
P_urn = binom(3,2)*binom(3,minor)*binom(3,major)*binom(3,bonus)/float(binom(n, n-i+1))
P_right_last_card = 1.0/(n-i+1)
probs[i]+=4*P_urn*P_right_last_card #factor 4 from symmetry
print "Expected number of cards:", sum((prob*card_cnt for card_cnt, prob in enumerate(probs)))
norm:=sum Wi for i in set
P(i|set)=Wi/norm if i not in set else 0.0
set_without_i:=set/{i}
P(set)=sum P(set_without_i)*P(i|set_without_i) for i in set
#calculates probability to end the game with 3 cards of a type
N=12
#set representation int->list
def decode_set(encoded):
decoded=[False]*N
for i in xrange(N):
if encoded&(1<<i):
decoded[i]=True
return decoded
weights = [170000, 170000, 105, 170000, 170000, 215, 150000, 150000, 12000, 105000, 105000, 105000]
def get_probs(decoded_set):
denom=float(sum((w for w,is_taken in zip(weights, decoded_set) if not is_taken)))
return [w/denom if not is_taken else 0.0 for w,is_taken in zip(weights, decoded_set)]
def end_group(encoded_set):
for i in xrange(4):
whole_group = 7<<(3*i) #7=..000111, 56=00111000 and so on
if (encoded_set & whole_group)==whole_group:
return i
return None
#MAIN: dynamic program:
MAX=(1<<N)#max possible set is 1<<N-1
probs=[0.0]*MAX
#we always start with the empty set:
probs[0]=1.0
#building bottom-up
for current_set in xrange(MAX):
if end_group(current_set) is None: #game not ended yet!
decoded_set=decode_set(current_set)
trans_probs=get_probs(decoded_set)
for i, is_set in enumerate(decoded_set):
if not is_set:
new_set=current_set | (1<<i)
probs[new_set]+=probs[current_set]*trans_probs[i]
#filtering wins:
group_probs=[0.0]*4
for current_set in xrange(MAX):
group_won=end_group(current_set)
if group_won is not None:
group_probs[group_won]+=probs[current_set]
print zip(["Grand", "Major", "Minor", "Bonus"], group_probs)
probs=[{}=00=0->1.0, 01={a}=1->0.0, {b}=10=2->0.0, {a,b}=11=3->0.0]
probs=[{}=00=0->1.0, 01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->0.0]
probs=[{}=00=0->1.0, 01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->0.66]
probs=[{}=00=0->1.0, 01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->1.0]