为什么在python 3中max(list,key)和max(set(list),key)之间有不同的结果?

时间:2019-06-13 15:29:58

标签: python-3.x list set max

我正在Udacity中学习CS212,并具有以下代码。


用户说明: 编写一个函数best_wild_hand(hand) 输入7张牌并返回最佳5张牌。 在这个问题上,一只手可能会包括 说笑话者小丑将被视为“通配符”, 可以采用相同颜色的任何等级或西服。的 黑色小丑'?B'可以用作任何黑桃或棍棒 红色的小丑'?R'可以用作任何心脏 或钻石。


代码如下:

import itertools

def best_wild_hand(hand):
    "Try all values for jokers in all 5-card selections."


    if '?B' not in hand and '?R' not in hand:
        return max(itertools.combinations(hand,5), key=hand_rank)


    black = [ i+j for i in list('23456789TJQKA') for j in ['S','C']]
    red = [ i+j for i in list('23456789TJQKA') for j in ['H','D']]

    cards = []
    hands = []


    if '?B' in hand and '?R' not in hand:
        for item in black:
            temp = hand[:]
            temp[temp.index('?B')] = item
            cards.append(temp) 
    elif '?R' in hand and '?B' not in hand:
        for item in red:
            temp = hand[:]
            temp[temp.index('?R')] = item
            cards.append(temp)
    else:
        for i in black:
            for j in red:
                temp = hand[:]
                temp[temp.index('?B')] = i
                temp[temp.index('?R')] = j
                cards.append(temp)
    #cards = set(cards)

    for card in cards:
        hands  += itertools.combinations(card, 5)

    #print(len(hands))
    #hands = set(hands)
    #print(len(hands))
    return max(hands, key=hand_rank)


def test_best_wild_hand():
    assert (sorted(best_wild_hand("6C 7C 8C 9C TC 5C ?B".split()))
            == ['7C', '8C', '9C', 'JC', 'TC'])
    assert (sorted(best_wild_hand("TD TC 5H 5C 7C ?R ?B".split()))
            == ['7C', 'TC', 'TD', 'TH', 'TS'])
    assert (sorted(best_wild_hand("JD TC TH 7C 7D 7S 7H".split()))
            == ['7C', '7D', '7H', '7S', 'JD'])
    return 'test_best_wild_hand passes'

# ------------------
# Provided Functions
# 
# You may want to use some of the functions which
# you have already defined in the unit to write 
# your best_hand function.

def hand_rank(hand):
    "Return a value indicating the ranking of a hand."
    ranks = card_ranks(hand) 
    if straight(ranks) and flush(hand):
        return (8, max(ranks))
    elif kind(4, ranks):
        return (7, kind(4, ranks), kind(1, ranks))
    elif kind(3, ranks) and kind(2, ranks):
        return (6, kind(3, ranks), kind(2, ranks))
    elif flush(hand):
        return (5, ranks)
    elif straight(ranks):
        return (4, max(ranks))
    elif kind(3, ranks):
        return (3, kind(3, ranks), ranks)
    elif two_pair(ranks):
        return (2, two_pair(ranks), ranks)
    elif kind(2, ranks):
        return (1, kind(2, ranks), ranks)
    else:
        return (0, ranks)

def card_ranks(hand):
    "Return a list of the ranks, sorted with higher first."
    ranks = ['--23456789TJQKA'.index(r) for r, s in hand]
    ranks.sort(reverse = True)
    return [5, 4, 3, 2, 1] if (ranks == [14, 5, 4, 3, 2]) else ranks

def flush(hand):
    "Return True if all the cards have the same suit."
    suits = [s for r,s in hand]
    return len(set(suits)) == 1

def straight(ranks):
    """Return True if the ordered 
    ranks form a 5-card straight."""
    return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5

def kind(n, ranks):
    """Return the first rank that this hand has 
    exactly n-of-a-kind of. Return None if there 
    is no n-of-a-kind in the hand."""
    for r in ranks:
        if ranks.count(r) == n: return r
    return None

def two_pair(ranks):
    """If there are two pair here, return the two 
    ranks of the two pairs, else None."""
    pair = kind(2, ranks)
    lowpair = kind(2, list(reversed(ranks)))
    if pair and lowpair != pair:
        return (pair, lowpair)
    else:
        return None 


print(test_best_wild_hand())

上面的代码工作正常。但是,当我将def best_wild_hand(hand)中的最后一行更改为

 return max(set(hands), key=hand_rank)

结果错误。为什么我将list转换为setmax()不起作用?谢谢

1 个答案:

答案 0 :(得分:1)

TLDR

集合中缺少顺序使得max(set(hands))的返回不稳定,有时返回错误答案,有时正确。

经过大量挖掘,我认为我已经找到了错误的来源。首先,您的逻辑是正确的:通过调用set(),您删除了重复项,因此无论是否存在重复项,结果都应该相同。

在原始代码中,如果第二个测试用例被注释掉,则使用set(hands)将通过其他用例(至少在我的环境中)。因此,问题可能出在第二个测试用例上。

如果您隔离出第二个测试用例,并使用set(hands)运行该程序20次,那么棘手的事情就会发生:大多数情况下,测试用例失败,但实际上几次通过!

这太疯狂了!相同的代码给出不同的结果。第二个测试用例的正确输出是('TD', 'TC', '7C', 'TH', 'TS')。但是max函数还将返回至少两个其他输出('TD', 'TC', '7C', 'TH', 'TC')('TD', 'TC', '7C', 'TD', 'TS')

如果在所有这三只指针上调用hand_rank()时都检查了返回值,则结果是相同的(7, 10, 7)。换句话说,这三只手的排名相同。因此,将哪个作为最大返回值取决于传递给max函数的是列表还是集合。

如果通过了列表(如hands的情况),则返回第一次出现的最大值。在这种情况下,('TD', 'TC', '7C', 'TH', 'TS')的索引是9081,('TD', 'TC', '7C', 'TH', 'TC')的索引是9627,('TD', 'TC', '7C', 'TD', 'TS')的索引是9102。因此,如果通过hands,则正确答案始终返回('TD', 'TC', '7C', 'TH', 'TS')

如果传递了一个集合,则由于没有内部排序,因此返回的值可能会有所不同,这正是我们观察到的行为。为了进行进一步的测试,您可以尝试以下方法:

max([(2,'a'),(2,'b'),(2,'c')],key=lambda x:x[0]) # output (2, 'a')
max({(2,'a'),(2,'b'),(2,'c')},key=lambda x:x[0]) # output (2, 'c') or any of the other two depending on implementation

总而言之,通过set(hands)时测试失败的原因是在多个元素具有相同排名时调用max on set的与实现有关的行为。但是,为了进一步推动这一问题,运气好的话,用hands返回正确的答案可能是纯粹的运气,因为如果在组合步骤中将错误的答案附加在正确的答案之前,那么错误的答案将是最大功能。我想说测试用例应该修改为包括所有这三只手,作为潜在的正确答案。