python - 生成所有 5 张牌扑克手

标签 python algorithm permutation combinatorics poker

这个问题乍看起来很简单,但实际上比看起来要复杂得多。这让我一时难过。

有 52c5 = 2,598,960 种方法可以从 52 张牌组中选择 5 张牌。然而,由于花色在扑克中是可以互换的,其中许多是等价的——手牌 2H 2C 3H 3S 4D 等价于 2D 2S 3D 3C 4H——只需交换花色即可。根据 wikipedia 的说法,一旦考虑了可能的花色重新着色,就有 134,459 个不同的 5 张牌手。

问题是,我们如何有效地生成所有这些可能的牌?我不想生成所有手牌,然后消除重复,因为我想将这个问题应用于更多数量的牌,以及评估快速螺旋失控的手牌数量。我目前的尝试集中在生成深度优先,并跟踪当前生成的卡片以确定哪些花色和等级对下一张卡片有效,或者广度优先,生成所有可能的下一张卡片,然后通过转换每个卡片来删除重复项通过重新着色交给“规范”版本。这是我在 Python 中尝试的广度优先解决方案:

# A card is represented by an integer. The low 2 bits represent the suit, while
# the remainder represent the rank.
suits = 'CDHS'
ranks = '23456789TJQKA'

def make_canonical(hand):
  suit_map = [None] * 4
  next_suit = 0
  for i in range(len(hand)):
    suit = hand[i] & 3
    if suit_map[suit] is None:
      suit_map[suit] = next_suit
      next_suit += 1
    hand[i] = hand[i] & ~3 | suit_map[suit]
  return hand

def expand_hand(hand, min_card):
  used_map = 0
  for card in hand:
    used_map |= 1 << card

  hands = set()
  for card in range(min_card, 52):
    if (1 << card) & used_map:
      continue
    new_hand = list(hand)
    new_hand.append(card)
    make_canonical(new_hand)
    hands.add(tuple(new_hand))
  return hands

def expand_hands(hands, num_cards):
  for i in range(num_cards):
    new_hands = set()
    for j, hand in enumerate(hands):
      min_card = hand[-1] + 1 if i > 0 else 0
      new_hands.update(expand_hand(hand, min_card))
    hands = new_hands
  return hands

不幸的是,这会产生太多手牌:

>>> len(expand_hands(set([()]), 5))
160537

谁能提出一个更好的方法来生成不同的手,或者指出我在尝试中出错的地方?

最佳答案

您的整体方法是合理的。我很确定问题出在您的 make_canonical 函数上。您可以尝试打印将 num_cards 设置为 3 或 4 的手牌,然后查找您错过的等价物。

我找到了一个,但可能还有更多:

# The inputs are equivalent and should return the same value
print make_canonical([8, 12 | 1]) # returns [8, 13]
print make_canonical([12, 8 | 1]) # returns [12, 9]

作为引用,以下是我的解决方案(在查看您的解决方案之前开发)。我使用深度优先搜索而不是广度优先搜索。此外,我没有编写将手转换为规范形式的函数,而是编写了一个函数来检查手是否是规范的。如果它不是规范的,我会跳过它。我定义了 rank = card % 13 和 suit = card/13。这些差异都不重要。

import collections

def canonical(cards):
    """
    Rules for a canonical hand:
    1. The cards are in sorted order

    2. The i-th suit must have at least many cards as all later suits.  If a
       suit isn't present, it counts as having 0 cards.

    3. If two suits have the same number of cards, the ranks in the first suit
       must be lower or equal lexicographically (e.g., [1, 3] <= [2, 4]).

    4. Must be a valid hand (no duplicate cards)
    """

    if sorted(cards) != cards:
        return False
    by_suits = collections.defaultdict(list)
    for suit in range(0, 52, 13):
        by_suits[suit] = [card%13 for card in cards if suit <= card < suit+13]
        if len(set(by_suits[suit])) != len(by_suits[suit]):
            return False
    for suit in range(13, 52, 13):
        suit1 = by_suits[suit-13]
        suit2 = by_suits[suit]
        if not suit2: continue
        if len(suit1) < len(suit2):
            return False
        if len(suit1) == len(suit2) and suit1 > suit2:
            return False
    return True

def deal_cards(permutations, n, cards):
    if len(cards) == n:
        permutations.append(list(cards))
        return
    start = 0
    if cards:
        start = max(cards) + 1
    for card in range(start, 52):
        cards.append(card)
        if canonical(cards):
            deal_cards(permutations, n, cards)
        del cards[-1]

def generate_permutations(n):
    permutations = []
    deal_cards(permutations, n, [])
    return permutations

for cards in generate_permutations(5):
    print cards

它生成正确数量的排列:

Cashew:~/$ python2.6 /tmp/cards.py | wc
134459

关于python - 生成所有 5 张牌扑克手,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3829457/

相关文章:

python - 正弦信号与矩形脉冲的卷积

algorithm - 一种高效的排序列表数据结构

c - 如何在另一个链表中获取链表的奇数 inode ?我不想使用双指针

algorithm - 向量化搜索包含给定子排列(带重复)的排列(带重复)

python - 如何在 python 中获取大小为 k 的列表的所有组合(其中 k > 列表的长度)?

python - pymongo + gevent : throw me a banana and just monkey_patch?

python - tensorflow boolean_mask 逆?

python - Python套件,软件包,模块,TestCase和TestSuite的差异

algorithm - 将 Excel 列号转换为 Matlab 中的列名

python - 在 python 中进行移位排列