python-优化排列搜索循环(不能使用itertools),速度非常慢.有什么建议么?

这是一个游戏,您有12张牌,然后选择您,直到从同一组中选择3张.我试图找到选择每个小组的可能性.我创建的脚本可以运行,但是速度非常慢.我的同事在R中创建了类似的脚本,但没有这些函数,而他的脚本所花费的时间是我的脚本的1/100.我只是想找出原因.任何想法将不胜感激.

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

“最大选择数”代表不同“卡”的总数.总选择数代表用户选择的最大数量.这是因为经过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')

解决方法:

好的,这次我希望我能解决您的问题:)

为了从算法上加快程序速度,需要两个洞察力(我想您是出于完整性的考虑而拥有它们):

>序列(card_1,card_2)和(card_2,card_1)的概率不相等,因此我们无法使用urn问题的结果,看来我们需要尝试所有排列.
>但是,考虑到到目前为止我们选择的一组纸牌,我们实际上并不需要这些信息以什么顺序选择它们-在以后的游戏中都是一样的.因此,使用动态编程并计算游戏期间要遍历的每个子集的概率就足够了(因此,我们需要检查2 ^ N而不是N!个状态).

对于一组拾取的卡,设置下一轮拾取i的概率为:

 norm:=sum Wi for i in set 
 P(i|set)=Wi/norm if i not in set else 0.0

计算P(set)的递归-游戏期间出现一组被选牌的概率为:

set_without_i:=set/{i}
P(set)=sum P(set_without_i)*P(i|set_without_i) for i in set

但是,仅应针对尚未结束游戏的set_without_i进行此操作,即,没有小组选择3张牌.

这可以通过递归记忆或通过使用自下而上的动态编程来实现.它还使用整数的二进制表示形式表示集合,并且(最重要的部分!)几乎立即返回结果[(‘Grand’,0.0014104762718021384),(‘Major’,0.0028878988709489244),(‘Minor’,0.15321793072867956),(‘奖金”,0.84248369412856905)]:

#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)

对代码中使用的“技巧”的一些解释:

一个非常标准的技巧是使用整数的二进制表示形式对集合进行编码.假设我们有对象[a,b,c],因此我们可以将集合{b,c}表示为110,这意味着a(列表中的第一个对应于0-最低数字)-不在集合中,集合中的b(1),集合中的c(1).但是,将110读为整数则是6.

current_set-for循环可模拟游戏,并在玩游戏时能更好地理解.让我们玩两张权重为[2,1]的卡片[a,b].

我们从一个空集开始游戏,0为整数,因此概率向量(给定集,其二进制表示形式以及作为映射到概率的整数):

  probs=[{}=00=0->1.0,  01={a}=1->0.0, {b}=10=2->0.0, {a,b}=11=3->0.0]

我们处理current_set = 0,有两种可能性分别是66%拿卡a和33%拿卡card,因此概率在处理后变为:

 probs=[{}=00=0->1.0,  01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->0.0]

现在我们处理current_set = 1 = {a},唯一的可能就是取b,所以我们将以集合{a,b}结尾.因此,我们需要通过公式更新其(3 = {a,b})概率,然后得出:

 probs=[{}=00=0->1.0,  01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->0.66]

在下一步中,我们处理2,给定集合{b}的唯一可能性是选择卡片a,因此需要再次更新集合{a,b}的概率

probs=[{}=00=0->1.0,  01={a}=1->0.66, {b}=10=2->0.33, {a,b}=11=3->1.0]

我们可以在两条不同的路径上到达{a,b}-这可以在我们的算法中看到.在我们游戏的某个时刻经过集合{a,b}的概率显然为1.0.

另一重要的事情是:在处理此集合之前,将处理所有通往{a,b}的路径(这将是下一步).

上一篇:python-在Pandas的3列的笛卡尔乘积后组织行


下一篇:Python:如何将生成器迭代值附加到列表