如何找到在二十一点获得21的方法数量?

时间:2016-08-24 01:04:57

标签: python oop combinations blackjack playing-cards

一些假设:

  1. 使用一副52张牌
  2. 图片卡数为10
  3. Aces数为1或11
  4. 顺序并不重要(即Ace + Queen与Queen + Ace相同)
  5. 我想我会顺序尝试所有可能的组合,看看哪些组合加起来21,但是有很多方法来混合卡片(52!方式)。这种方法也没有考虑到订单不重要,也没有考虑到任何一张卡(Spade,Club,Diamond,Heart)只有4种最大类型的事实。

    现在我正在考虑这样的问题:

    我们有11个“插槽”。这些插槽中的每一个都可以有53个可能的东西:52个卡中的1个或根本没有卡。它是11个插槽的原因是因为11张牌是可以发牌的最大牌数,但仍然加起来21;超过11张卡必须加起来超过21张。

    然后“最左边”的插槽将增加1,并且将检查所有11个插槽以查看它们是否加起来为21(0表示插槽中没有卡)。如果没有,右边的下一个插槽将递增,而下一个插槽将递增,依此类推。

    一旦前4个插槽包含相同的“卡”(在四个增量之后,前4个插槽全部为1),第五个插槽也不能是那个数字,因为有4个任意类型的数字。第五个插槽将成为剩余可用卡中的下一个最低编号;在四个1的情况下,第五个插槽将变为2,依此类推。

    你会如何做到这一点?

3 个答案:

答案 0 :(得分:4)

通过利用知识来划分和征服,如果你有13并选择10,你只需要选择卡总和到3左看......转发这个解决方案可能很慢(我花了大约180秒)盒子......它绝对不是最佳的)..

def sum_to(x,cards):
    if x == 0: # if there is nothing left to sum to
        yield []

    for i in range(1,12): # for each point value 1..11 (inclusive)
        if i  > x: break # if i is bigger than whats left we are done
        card_v = 11 if i == 1 else i
        if card_v not in cards: continue  # if there is no more of this card
        new_deck = cards[:] # create a copy of hte deck (we do not want to modify the original)
        if i == 1: # one is clearly an ace...
           new_deck.remove(11)
        else: # remove the value
           new_deck.remove(i)
        # on the recursive call we need to subtract our recent pick
        for result in sum_to(x-i,new_deck):
            yield [i] + result # append each further combination to our solutions

按如下方式设置您的卡

deck = []
for i in range(2,11): # two through ten (with 4 of each)
    deck.extend([i]*4)

deck.extend([10]*4) #jacks
deck.extend([10]*4) #queens
deck.extend([10]*4) #kings
deck.extend([11]*4) # Aces

然后只需调用你的函数

for combination in sum_to(21,deck):
    print combination

不幸的是,这确实允许一些重复的潜入... 为了获得唯一的条目,你需要稍微改变一下

在最后一行的sum_to中将其更改为

  # sort our solutions so we can later eliminate duplicates
  yield sorted([i] + result) # append each further combination to our solutions

然后当你得到你的组合时,你必须做一些深色巫毒风格的蟒蛇

 unique_combinations = sorted(set(map(tuple,sum_to(21,deck))),key=len,reverse=0)

 for combo in unique_combinations: print combo

从这个很酷的问题我已经了解了以下内容(请记住,在实际游戏中你会让经销商和其他玩家也从同一套牌中移除)

there are 416 unique combinations of a deck of cards that make 21
there are 300433 non-unique combinations!!!

the longest number of ways to make 21 are as follows
with 11 cards there are 1 ways
[(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3)]
with 10 cards there are 7 ways
with 9 cards there are 26 ways
with 8 cards there are 54 ways
with 7 cards there are 84 ways
with 6 cards there are 94 ways
with 5 cards there are 83 ways
with 4 cards there are 49 ways
with 3 cards there are 17 ways
with 2 cards there are 1 ways
[(10, 11)]

there are 54 ways in which all 4 aces are used in making 21!!
there are 106 ways of making 21 in which NO aces are used !!!

请记住,这些通常是次优的游戏(即考虑A,10 - > 1,10并且击中)

答案 1 :(得分:1)

在担心价值为10的套装和不同卡片之前,请先确定21产生的不同价值组合的数量。例如,5, 5, 10, 1就是这样一种组合。以下函数采用limit作为目标值,start表示可以选择的最低值,used表示已选择的值列表:

def combinations(limit, start, used):
    # Base case
    if limit == 0:
        return 1

    # Start iteration from lowest card to picked so far
    # so that we're only going to pick cards 3 & 7 in order 3,7
    res = 0
    for i in range(start, min(12, limit + 1)):
        # Aces are at index 1 no matter if value 11 or 1 is used
        index = i if i != 11 else 1

        # There are 16 cards with value of 10 (T, J, Q, K) and 4 with every
        # other value
        available = 16 if index == 10 else 4
        if used[index] < available:
            # Mark the card used and go through combinations starting from
            # current card and limit lowered by the value
            used[index] += 1
            res += combinations(limit - i, i, used)
            used[index] -= 1

    return res

print combinations(21, 1, [0] * 11) # 416

由于我们对不同的卡组合而不是不同的值组合感兴趣,因此应修改上述基本情况以返回可用于生成值组合的不同卡组合的数量。幸运的是,这个非常简单的任务,Binomial coefficient可用于确定可以从k个项目中挑选n个项目的不同组合。

一旦知道used中每个值的不同卡组合的数量,它们就可以相互相乘以得到最终结果。因此,对于5, 5, 10, 15结果为bcoef(4, 2) == 6的结果,值为10bcoef(16, 1) == 16,值为1bcoef(4, 1) == 4。对于所有其他值bcoef(x, 0),结果为1。将这些值乘以结果6 * 16 * 4 == 384,然后返回:

import operator
from math import factorial

def bcoef(n, k):
    return factorial(n) / (factorial(k) * factorial(n - k))

def combinations(limit, start, used):
    if limit == 0:
        combs = (bcoef(4 if i != 10 else 16, x) for i, x in enumerate(used))
        res = reduce(operator.mul, combs, 1)
        return res

    res = 0
    for i in range(start, min(12, limit + 1)):
        index = i if i != 11 else 1
        available = 16 if index == 10 else 4

        if used[index] < available:
            used[index] += 1
            res += combinations(limit - i, i, used)
            used[index] -= 1

    return res

print combinations(21, 1, [0] * 11) # 186184

答案 2 :(得分:1)

所以我决定编写一个脚本,可以检查每个可能的可行手。总数是188052.由于我检查了每个可能的组合,这是确切的数字(而不是估计):

import itertools as it
big_list = []

def deck_set_up(m):
    special = {8:'a23456789TJQK', 9:'a23456789', 10:'a2345678', 11:'a23'} 
    if m in special:
        return [x+y for x,y in list(it.product(special[m], 'shdc'))]
    else:
        return [x+y for x,y in list(it.product('a23456789TJQKA', 'shdc'))]

deck_dict = {'as':1,'ah':1,'ad':1,'ac':1,
             '2s':2,'2h':2,'2d':2,'2c':2,
             '3s':3,'3h':3,'3d':3,'3c':3,
             '4s':4,'4h':4,'4d':4,'4c':4,
             '5s':5,'5h':5,'5d':5,'5c':5,
             '6s':6,'6h':6,'6d':6,'6c':6,
             '7s':7,'7h':7,'7d':7,'7c':7,
             '8s':8,'8h':8,'8d':8,'8c':8,
             '9s':9,'9h':9,'9d':9,'9c':9,
             'Ts':10,'Th':10,'Td':10,'Tc':10,
             'Js':10,'Jh':10,'Jd':10,'Jc':10,
             'Qs':10,'Qh':10,'Qd':10,'Qc':10,
             'Ks':10,'Kh':10,'Kd':10,'Kc':10,
             'As':11,'Ah':11,'Ad':11,'Ac':11}

stop_here = {2:'As', 3:'8s', 4:'6s', 5:'4h', 6:'3c', 7:'3s', 8:'2h', 9:'2s', 10:'2s', 11:'2s'}

for n in range(2,12):  # n is number of cards in the draw
    combos = it.combinations(deck_set_up(n), n)
    stop_point = stop_here[n]
    while True:
        try:
            pick = combos.next()
        except:
            break
        if pick[0] == stop_point:
            break                   
        if n < 8:
            if len(set([item.upper() for item in pick])) != n:
                continue
        if sum([deck_dict[card] for card in pick]) == 21:
            big_list.append(pick)
    print n, len(big_list)  # Total number hands that can equal 21 is 188052

在输出中,第一列是绘图中的卡数,第二列是累计数。所以&#34; 3&#34;之后的数字在输出中是2张牌的抽牌总数和21张牌。小写字母a是低ace(1点),大写字母A是高ace。我有一条线(带有set命令的线),以确保它抛出任何有重复卡的牌。

该脚本需要36分钟才能运行。因此,在执行时间和准确性之间肯定存在权衡。 &#34; big_list&#34;包含解决方案(即总和为21的每一手)

>>> 
================== RESTART: C:\Users\JBM\Desktop\bj3.py     ==================
2 64
3 2100
4 14804
5 53296
6 111776
7 160132
8 182452
9 187616
10 188048
11 188052    # <-- This is the total count, as these numbers are cumulative
>>>