Nic*_*son 38 python algorithm poker permutation combinatorics
乍一看这个问题听起来很简单,但事实证明它看起来要复杂得多.这让我很难过.
有52c5 = 2,598,960种方法可以从52张牌中选择5张牌.然而,由于套装在扑克中是可以互换的,所以其中许多都是等同的 - 手2H 2C 3H 3S 4D相当于2D 2S 3D 3C 4H - 简单地换掉套装.根据维基百科,一旦你考虑到可能的套装重新着色,有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
Run Code Online (Sandbox Code Playgroud)
不幸的是,这产生了太多的手:
>>> len(expand_hands(set([()]), 5))
160537
Run Code Online (Sandbox Code Playgroud)
任何人都可以建议一个更好的方法来产生独特的手,或指出我在尝试中出错的地方?
Dan*_*ach 18
你的整体方法是合理的.我很确定问题在于你的make_canonical功能.您可以尝试使用设置为3或4的num_cards打印出手,并查找您错过的等效项.
我发现了一个,但可能还有更多:
# 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]
Run Code Online (Sandbox Code Playgroud)
作为参考,下面是我的解决方案(在查看您的解决方案之前开发).我使用深度优先搜索而不是广度优先搜索.此外,我写了一个函数来检查一只手是否规范,而不是编写一个函数来将一个手转换为规范形式.如果它不是规范的,我会跳过它.我定义了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
Run Code Online (Sandbox Code Playgroud)
它会生成正确的排列数:
Cashew:~/$ python2.6 /tmp/cards.py | wc
134459
Run Code Online (Sandbox Code Playgroud)