策划极大极小算法极小、算法

2023-09-11 04:52:43 作者:扑进先生怀里

我想在Python高德纳的算法不超过5移动实施codebreaking主谋。我检查我的code几次,似乎遵循的算法,因为其既定的位置: http://en.wikipedia.org/wiki/Mastermind_(board_game)#Five -guess_algorithm

不过,我得到了一些秘密需要7甚至8移动来完成。这里是code:

  #returns多少公牛和母牛有
高清HowManyBc(猜的,秘密的):
    无效= MAX(猜测)+1
    公牛= 0
    牛= 0
    使r = 0
    而为r 4:
        如果猜测[R] ==秘密[R]:
            公牛=多头+ 1
            秘密[R] =无效
            猜[R] =无效
        R = R + 1个
    使r = 0
    而为r 4:
        p = 0时
        而P< 4:
            如果猜测[R] ==秘密[P]和猜测[R] =无效!
                牛=牛+ 1
                秘密[P] =无效
                打破
            p值= P + 1个
        R = R + 1个
    返回[公牛,母牛]

#发送的每个BC其在HMList指数
高清调整(BC1):
    如果BC1 == [0,0]:
        返回0
    ELIF BC1 == [0,1]:
        返回1
    ELIF BC1 == [0,2]:
        返回2
    ELIF BC1 == [0,3]:
       返回3
    ELIF BC1 == [0,4]:
        返回4
    ELIF BC1 == [1,0]:
        返回5
    ELIF BC1 == [1,1]:
        返回6
    ELIF BC1 == [1,2]:
        返回7
    ELIF BC1 == [1,3]:
        返回8
    ELIF BC1 == [2,0]:
        返回9
    ELIF BC1 == [2,1]:
        返回10
    ELIF BC1 == [2,2]:
        返回11
    ELIF BC1 == [3,0]:
        返回12
    ELIF BC1 == [4,0]:
    返回13
#将在HMList各项指标到公元前
高清AdjustmentInverse(的地方):
    如果地方== 0:
        返回[0,0]
    ELIF地方== 1:
        返回[0,1]
    ELIF地方== 2:
        返回[0,2]
    ELIF地方== 3:
        返回[0,3]
    ELIF地方== 4:
        返回[0,4]
    ELIF地方== 5:
        返回[1,0]
    ELIF地方== 6:
        返回[1,1]
    ELIF地方== 7:
        返回[1,2]
    ELIF地方== 8:
        返回[1,3]
    ELIF地方== 9:
        返回[2,0]
    ELIF地方== 10:
        返回[2,1]
    ELIF地方== 11:
        返回[2,2]
    ELIF地方== 12:
        返回[3,0]
    ELIF地方== 13:
        返回[4,0]
#给出了最低的肯定列表,而不包括其零
高清MinimumNozeros(List1中):
    最小= MAX(List1中)+1
    为List1中的项目:
        !如果项目= 0和项目<最低:
            最小=项目
    回报最低

#list所有可能的猜测
allList = []
为I0的范围内(0.6):
    为I1的范围内(0.6):
        为I2的范围内(0.6):
            为I3的范围内(0.6):
                allList.append([I0,I1,I2,I3])
TempList = [[0,0,5,4]]
秘密在TempList:
    猜= [0,0,1,1]
    BC = HowManyBc(猜[:],秘[:])
    计数器= 1
    optionList = []
    为I0的范围内(0.6):
        为I1的范围内(0.6):
            为I2的范围内(0.6):
                为I3的范围内(0.6):
                    optionList.append([I0,I1,I2,I3])
    而BC = [4,0]!
        dummyList = []与可能的密码,这个猜测#list
        为I0的范围内(0.6):
            为I1的范围内(0.6):
                为I2的范围内(0.6):
                    为I3的范围内(0.6):
                        opSecret = [I0,I1,I2,I3]
                        如果HowManyBc(猜[:],opSecret [:])== BC:
                            dummyList.append(opSecret)
        List1中= [项目的项目在optionList如果dummyList项目]
        optionList = List1的[:] optionList和dummyList的#交集
        item1Max = 0
        为ITEM1在allList:
            ListBC = []在optionList#[所有[公牛列表,牛]
            在optionList ITEM2:
                ListBC.append(HowManyBc(ITEM1 [:],ITEM2 [:]))
            HMList = [0] * 14#数量有多少B / C有在optionList ITEM2
            为BC1在ListBC:
                指数=调整(BC1)
                HMList [指数] = HMList [指数] +1
            M = MAX(HMList)#maximum [B,C],更左 - 少消除(最小的极大极小)
            maxList = [我为I,J在历数(HMList),在j == M]
            maxElement = maxList [0] #index,最大
            possibleGuess = []
            如果M> item1Max:#max的猜测,最大的极小
                item1Max = M
                possibleGuess = [I [:]因为我在optionList如果AdjustmentInverse(maxElement)== HowManyBc(ITEM1 [:],我[:])]
                nextGuess = possibleGuess [0] [:]
        猜= nextGuess [:]
        BC = HowManyBc(猜[:],秘[:])
        计数器=计数器+ 1
 

我得到:

有[5,3,3,4〕计数器是7

基于贪心策略直接搜索算法和极大极小博弈树算法的智能人机博弈五子棋游戏...

有[5,4,4,5〕计数器是8

如果有人可以帮助我将AP preciate非常喜欢!

谢谢,迈克

解决方案

1。有什么不对您的实现

有四个差错。

该评论是错误的在这条线:

  M = MAX(HMList)#maximum [B,C],更左 - 少消除(最小的极大极小)
 

这实际上是在极小的最大(这应该是从通话清晰最高)。您正在试图发现的的猜测最小化的,产生的相同的评估可能的秘密团体的的最大尺寸的。在这里,我们发现了群体的最大尺寸,所以这是最大。

这个错误导致你做这个:

 如果M> item1Max:#max的猜测,最大的极小
 

在这里,你需要采取的分,而不是最大。

在以下几行你挑中 optionList 中的第一项,将产生同样的评价为 ITEM1

  possibleGuess = [I [:]因为我在optionList如果AdjustmentInverse(maxElement)== HowManyBc(ITEM1 [:],我[:])]
nextGuess = possibleGuess [0] [:]
 

但是,这不是正确的:我们希望在这里的猜测是 ITEM1 ,而不是将产生同样的评价其他一些猜测

最后,你不妥善处理此案,其中 optionList 只剩下一个项目。在这种情况下,所有可能的猜测是,在这个项目中区分一样好,所以极大极小过程不会对猜测区分。在这种情况下,你应该只是猜测 optionList [0]

2。在您code。其他的评论

变量名都很差选择。例如,什么是 ITEM1 ?这是您要评估的猜测,所以当然它应该被称为像 possible_guess ?我怀疑自己的错误§1.3上述被部分变量名这个可怜的选择所引起的。

有大量不必要的复制。您所有的 [:] 是毫无意义的,可以拆卸。变量的List1 也是毫无意义(为什么不直接分配给 optionList ?),因为是 nextGuess (这不只是分配给猜到?)

您打造 dummyList 包括所有可能的秘密,将匹配的最后一个猜测,但你扔掉所有条目 dummyList 是不是也在 optionList 。那么,为什么不只是遍历 optionList 并保持匹配的项?像这样的:

  optionList = [在optionList项项目,如果HowManyBc(猜,项目)== BC]
 

您建立一个表 HMList 这计数事件公牛和母牛的每个图案的数量。你注意到的事实,有14种可能(牛,牛)对等你写的功能调整 AdjustmentInverse 映射来回(牛,牛)对和其索引列表之间。

这些功能可以有更简单的实现,如果你把一个数据驱动的方法,并使用内置的 list.index 方式:

 #注意,(3,1)是不可能的。
评价= [(0,0),(0,1),(0,2),(0,3),(0,4),(1,0),(1,1),
               (1,2),(1,3),(2,0),(2,1),(2,2),(3,0),(4,0)]

高清调整(评价):
    返回EVALUATIONS.index(评估)

高清AdjustmentInverse(指数):
    回报评估[指数]
 

不过,修复错误后§1.3上面,你不需要 AdjustmentInverse 了。而调整是可以避免的,如果你在的 collections.Counter 的而不是一个列表。因此,而不是:

  HMList = [0] * 14#数量有多少B / C有在optionList ITEM2
为BC1在ListBC:
    指数=调整(BC1)
    HMList [指数] = HMList [指数] +1
M = MAX(HMList)
 

您可以简单地写:

  M = MAX(计数器(ListBC).values​​())
 

3。改进code

评估猜测(你的函数 HowManyBc )可以降低到使用类的 collections.Counter 从标准库。

 从收藏导入计数器

高清评估(猜的,秘密的):
    返回对(公牛,母牛),其中`bulls`是的计数
    在`guess`字符出现在`secret`同一位置
    和`cows`是`guess`出现在字符计数
    在`secret`不同的位置。

        >>>评估(ABCD,ACAD)
        (2,1)
        >>>评估(ABAB,AABB)
        (2,2)
        >>>评估(ABCD,DCBA)
        (0,4)

    
    匹配= SUM((计数器(秘密)及计数器(猜测))值()。)
    公牛= SUM(C ==克C,在压缩克(秘密,猜测))
    回到公牛队,比赛 - 公牛
 

我碰巧preFER使用字母为codeS的主谋。 ACDB 这么多更好阅读和类型而不是 [0,2,3,1] 。但我的评估为您重新present他们作为比较的序列的功能是灵活的,以你如何重新present的codeS和猜测,只要项目,所以你可以用号码清单,如果你preFER。

还请注意,我已经写了一些文档测试:这是一个快速办法同时提供实例文档和测试功能。

函数 itertools.product 提供了一个方便的方式来建立的codeS列表中,而无需编写四个嵌套循环:

 从itertools进口产品
ALL_ codeS = [''。加入(三)对C的产品(ABCDEF,重复= 4)]
 

Knuth的五猜测算法使用极小极大原则。那么,为什么不通过采取 分调用序列的以 最高

 高清高德纳(秘密):
    给定的秘密运行Knuth的5猜测算法。
    断言(秘密ALL_ codeS)
    codeS = ALL_ codeS
    键=拉姆达G:最大值(计数器(评估(G,C)对于C在codeS).values​​())
    猜=AABB
    而真正的:
        反馈=评估(猜,秘密)
        打印(我想{}:反馈{}格式(猜,反馈)。)
        如果猜测==秘诀:
            打破
        codeS = [C对C在codeS,如果评估(猜,C)==反馈]
        如果len(codeS)== 1:
            猜= codeS [0]
        其他:
            猜=分钟(ALL_ codeS,键=键)
 

下面是一个例子来看:

>>>高德纳('FEDA) 猜猜AABB:反馈(0,1) 猜猜BCDD:反馈(1,0) 猜猜AEAC:反馈(1,1) 猜猜AFCC:反馈(0,2) 猜猜FEDA:反馈(4,0)

I am trying to implement in python Donald Knuth's algorithm for codebreaking mastermind in not more than 5 moves. I have checked my code several times, and it seems to follow the algorithm, as its stated here: http://en.wikipedia.org/wiki/Mastermind_(board_game)#Five-guess_algorithm

However, I get that some of the secrets take 7 or even 8 moves to accomplish. Here is the code:

#returns how many bulls and cows there are
def HowManyBc(guess,secret):
    invalid=max(guess)+1
    bulls=0
    cows=0
    r=0
    while r<4:
        if guess[r]==secret[r]:
            bulls=bulls+1
            secret[r]=invalid
            guess[r]=invalid
        r=r+1
    r=0
    while r<4:
        p=0
        while p<4:
            if guess[r]==secret[p] and guess[r]!=invalid:
                cows=cows+1
                secret[p]=invalid
                break
            p=p+1
        r=r+1    
    return [bulls,cows]

# sends every BC to its index in HMList
def Adjustment(BC1):
    if BC1==[0,0]:
        return 0
    elif BC1==[0,1]:
        return 1
    elif BC1==[0,2]:
        return 2
    elif BC1==[0,3]:
       return 3
    elif BC1==[0,4]:
        return 4
    elif BC1==[1,0]:
        return 5
    elif BC1==[1,1]:
        return 6
    elif BC1==[1,2]:
        return 7
    elif BC1==[1,3]:
        return 8
    elif BC1==[2,0]:
        return 9
    elif BC1==[2,1]:
        return 10
    elif BC1==[2,2]:
        return 11
    elif BC1==[3,0]:
        return 12
    elif BC1==[4,0]:
    return 13
# sends every index in HMList to its BC
def AdjustmentInverse(place):
    if place==0:
        return [0,0]
    elif place==1:
        return [0,1]
    elif place==2:
        return [0,2]
    elif place==3:
        return [0,3]
    elif place==4:
        return [0,4]
    elif place==5:
        return [1,0]
    elif place==6:
        return [1,1]
    elif place==7:
        return [1,2]
    elif place==8:
        return [1,3]
    elif place==9:
        return [2,0]
    elif place==10:
        return [2,1]
    elif place==11:
        return [2,2]
    elif place==12:
        return [3,0]
    elif place==13:
        return [4,0]   
# gives minimum of positive list without including its zeros    
def MinimumNozeros(List1):
    minimum=max(List1)+1
    for item in List1:
        if item!=0 and item<minimum:
            minimum=item
    return minimum

#list with all possible guesses
allList=[]
for i0 in range(0,6):
    for i1 in range(0,6):
        for i2 in range(0,6):
            for i3 in range(0,6):
                allList.append([i0,i1,i2,i3])
TempList=[[0,0,5,4]]
for secret in TempList:
    guess=[0,0,1,1]
    BC=HowManyBc(guess[:],secret[:])
    counter=1
    optionList=[]
    for i0 in range(0,6):
        for i1 in range(0,6):
            for i2 in range(0,6):
                for i3 in range(0,6):
                    optionList.append([i0,i1,i2,i3])
    while BC!=[4,0]:
        dummyList=[] #list with possible secrets for this guess
        for i0 in range(0,6):
            for i1 in range(0,6):
                for i2 in range(0,6):
                    for i3 in range(0,6):
                        opSecret=[i0,i1,i2,i3]
                        if HowManyBc(guess[:],opSecret[:])==BC:
                            dummyList.append(opSecret)
        List1=[item for item in optionList if item in dummyList]
        optionList=List1[:] # intersection of optionList and dummyList
        item1Max=0
        for item1 in allList:
            ListBC=[] # [list of all [bulls,cows] in optionList
            for item2 in optionList:
                ListBC.append(HowManyBc(item1[:],item2[:]))
            HMList=[0]*14 # counts how many B/C there are for item2 in optionList
            for BC1 in ListBC:
                index=Adjustment(BC1)
                HMList[index]=HMList[index]+1
            m=max(HMList)#maximum [B,C], more left - less eliminated (min in minimax)
            maxList=[i for i, j in enumerate(HMList) if j == m]
            maxElement=maxList[0] #index with max
            possibleGuess=[]
            if m>item1Max: #max of the guesses, the max in minimax
                item1Max=m
                possibleGuess=[i[:] for i in optionList if   AdjustmentInverse(maxElement)==HowManyBc(item1[:],i[:])]
                nextGuess=possibleGuess[0][:]
        guess=nextGuess[:]
        BC=HowManyBc(guess[:],secret[:])
        counter=counter+1

I get:

for [5, 3, 3, 4] counter is 7

for [5, 4, 4, 5] counter is 8

If someone could help I would appreciate it very much!

Thanks,mike

解决方案

1. What's wrong with your implementation

There are four mistakes.

The comment is wrong on this line:

m=max(HMList)#maximum [B,C], more left - less eliminated (min in minimax)

This is actually the "max" in the minimax (which should have been clear from the call to max). You are trying to find the guess that minimizes the maximum size of the groups of possible secrets that yield the same evaluation. Here we are finding the maximum size of the groups, so that's the "max".

That mistake caused you to make this one:

if m>item1Max: #max of the guesses, the max in minimax

Here you need to take the min, not the max.

On the following lines you pick the first item among optionList that would generate the same evaluation as item1:

possibleGuess=[i[:] for i in optionList if   AdjustmentInverse(maxElement)==HowManyBc(item1[:],i[:])]
nextGuess=possibleGuess[0][:]

But that's not right: the guess we want here is item1, not some other guess that would generate the same evaluation!

Finally, you don't properly handle the case where optionList has only one remaining item. In this case all possible guesses are equally good at distinguishing among this item, so the minimax procedure doesn't differentiate between the guesses. In this case you should just guess optionList[0].

2. Other comments on your code

The variable names are poorly chosen. For example, what is item1? This is the guess that you are evaluating, so surely it should be called something like possible_guess? I suspect that your mistake §1.3 above was partly caused by this poor choice of variable name.

There's vast amounts of needless copying. All of your [:] are pointless and can be removed. The variable List1 is also pointless (why not just assign to optionList?), as is nextGuess (which not just assign to guess?)

You build dummyList consisting of all possible secrets that would match the last guess, but then you throw away all the entries in dummyList that aren't also in optionList. So why not just loop over optionList and keep the entries that match? Like this:

optionList = [item for item in optionList if HowManyBc(guess, item)==BC]

You build up a table HMList which counts the number of occurrences of each pattern of bulls and cows. You have noted the fact that there are 14 possible (bull, cow) pairs and so you've written the functions Adjustment and AdjustmentInverse to map back and forth between (bull, cow) pairs and their indices in the list.

These functions could have much simpler implementations if you took a data-driven approach and used the built-in list.index method:

# Note that (3, 1) is not possible.
EVALUATIONS = [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1),
               (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (3, 0), (4, 0)]

def Adjustment(evaluation):
    return EVALUATIONS.index(evaluation)

def AdjustmentInverse(index):
    return EVALUATIONS[index]

But after fixing mistake §1.3 above, you don't need AdjustmentInverse any more. And Adjustment could be avoided if you kept the counts in a collections.Counter instead of a list. So instead of:

HMList=[0]*14 # counts how many B/C there are for item2 in optionList
for BC1 in ListBC:
    index=Adjustment(BC1)
    HMList[index]=HMList[index]+1
m=max(HMList)

you could simply write:

m = max(Counter(ListBC).values())

3. Improved code

Evaluating a guess (your function HowManyBc) can be reduced to just three lines using the class collections.Counter from the standard library.

from collections import Counter

def evaluate(guess, secret):
    """Return the pair (bulls, cows) where `bulls` is a count of the
    characters in `guess` that appear at the same position in `secret`
    and `cows` is a count of the characters in `guess` that appear at
    a different position in `secret`.

        >>> evaluate('ABCD', 'ACAD')
        (2, 1)
        >>> evaluate('ABAB', 'AABB')
        (2, 2)
        >>> evaluate('ABCD', 'DCBA')
        (0, 4)

    """
    matches = sum((Counter(secret) & Counter(guess)).values())
    bulls = sum(c == g for c, g in zip(secret, guess))
    return bulls, matches - bulls

I happen to prefer using letters for the codes in Mastermind. ACDB is so much nicer to read and type than [0, 2, 3, 1]. But my evaluate function is flexible as to how you represent the codes and guesses, as long as you represent them as sequences of comparable items, so you can use lists of numbers if you prefer.

Notice also that I've written some doctests: these are a quick way to simultaneously provide examples in the documentation and to test the function.

The function itertools.product provides a convenient way to build the list of codes without having to write four nested loops:

from itertools import product
ALL_CODES = [''.join(c) for c in product('ABCDEF', repeat=4)]

Knuth's five-guess algorithm uses the minimax principle. So why not implement it by taking the min of a sequence of calls to max?

def knuth(secret):
    """Run Knuth's 5-guess algorithm on the given secret."""
    assert(secret in ALL_CODES)
    codes = ALL_CODES
    key = lambda g: max(Counter(evaluate(g, c) for c in codes).values())
    guess = 'AABB'
    while True:
        feedback = evaluate(guess, secret)
        print("Guess {}: feedback {}".format(guess, feedback))
        if guess == secret:
            break
        codes = [c for c in codes if evaluate(guess, c) == feedback]
        if len(codes) == 1:
            guess = codes[0]
        else:
            guess = min(ALL_CODES, key=key)

Here's an example run:

>>> knuth('FEDA')
Guess AABB: feedback (0, 1)
Guess BCDD: feedback (1, 0)
Guess AEAC: feedback (1, 1)
Guess AFCC: feedback (0, 2)
Guess FEDA: feedback (4, 0)

 
精彩推荐