一些假设:
我想我会顺序尝试所有可能的组合,看看哪些组合加起来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,依此类推。
你会如何做到这一点?
答案 0 :(得分:4)
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, 1
值5
结果为bcoef(4, 2) == 6
的结果,值为10
至bcoef(16, 1) == 16
,值为1
至bcoef(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
>>>