почему разные результаты между max (список, ключ) и max (набор (список), ключ) в Python 3? - PullRequest
0 голосов
/ 13 июня 2019

Я изучаю CS212 в Udacity и имею следующий код.


Инструкции для пользователя: напишите функцию 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 в set, а max() не работает?Спасибо

1 Ответ

1 голос
/ 15 июня 2019

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, зависит от того, передан ли он списку или набору функции 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 на множестве, когда несколько элементов имеют одинаковое ранжирование.,Но чтобы продвинуть эту проблему дальше, может быть, просто удача, что правильный ответ возвращается с hands, потому что, если во время шага комбинации неправильный ответ добавляется перед правильным ответом, неправильный ответ будет возвращен функцией max.,Я бы сказал, что контрольный пример следует изменить, чтобы включить все эти три руки в качестве потенциального правильного ответа.

...