【问题标题】:why different result between max(list,key) and max(set(list), key) in python 3?为什么在 python 3 中 max(list,key) 和 max(set(list), key) 的结果不同?
【发布时间】:2019-06-13 15:29:58
【问题描述】:

我在 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() 不起作用?谢谢

【问题讨论】:

  • ok 首先,如果你想在一个列表中找到最大值,不需要使用 set(),其次,我们需要知道列表“hands”里面是什么(它是一个元组列表? 字典列表?) 否则无法提供帮助。
  • @Inspi,我转为set,因为hands 可能有重复元素。 hands 也是一个元组列表,例如:[('6C', '7C', '8C', '9C', 'TC'), ('6C', '7C', '8C', '9C', '5C')]。谢谢
  • 我很确定使用 set() 会比保持列表原样慢,即使有重复,你确定函数 hand_rank 像你打算的那样工作吗?
  • @Inspi,是的,hand_rank 是由 udacity 提供的。我很惊讶将list 转换为mapmax() 不起作用。
  • 你还应该检查一下手上是否装满了你想要的手

标签: python-3.x list set max


【解决方案1】:

TLDR

set中缺少排序使得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 返回取决于它是传递给 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返回正确答案可能是纯粹的运气,因为如果在组合步骤中错误的答案被附加在正确答案之前,那么max函数会返回错误的答案.我想说应该修改测试用例,以将所有这三只手作为潜在的正确答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    相关资源
    最近更新 更多