平常在外漂泊,每天都是工作、工作、工作!不会接触到打牌娱乐啥的,可是回家过年的时候,难免会和亲戚朋友聚聚打打牌啥的。想要少输点不得从网上找找攻略啊,这不,在GitHub上就有,嘿嘿~~
Github上面这个项目模拟了2000多万局炸金花,计算了不同牌在不同玩家数下的炸金花胜率,并且绘制了一个胜率表:
2人 | 3人 | 4人 | 5人 | 6人 | 7人 | 8人 | 9人 | 10人 | |
---|---|---|---|---|---|---|---|---|---|
杂牌7 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
杂牌10 | 13 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
杂牌Q | 31 | 9 | 3 | 1 | 0 | 0 | 0 | 0 | 0 |
杂牌K | 44 | 19 | 8 | 3 | 1 | 0 | 0 | 0 | 0 |
杂牌A | 60 | 36 | 21 | 12 | 7 | 4 | 2 | 1 | 0 |
对2 | 74 | 55 | 41 | 31 | 23 | 17 | 13 | 9 | 7 |
对5 | 78 | 61 | 48 | 37 | 29 | 23 | 18 | 14 | 11 |
对8 | 82 | 68 | 56 | 46 | 38 | 31 | 26 | 21 | 17 |
对10 | 85 | 73 | 62 | 53 | 45 | 39 | 33 | 28 | 24 |
对Q | 88 | 78 | 69 | 61 | 54 | 48 | 42 | 37 | 33 |
对A | 91 | 83 | 75 | 69 | 62 | 57 | 52 | 47 | 43 |
顺456 | 92 | 85 | 78 | 72 | 66 | 61 | 56 | 51 | 48 |
顺789 | 93 | 87 | 81 | 75 | 70 | 65 | 61 | 57 | 52 |
顺JQK | 94 | 89 | 84 | 79 | 75 | 71 | 65 | 63 | 59 |
同花10 | 95 | 91 | 87 | 83 | 79 | 75 | 72 | 68 | 65 |
同花Q | 96 | 93 | 90 | 87 | 84 | 81 | 78 | 76 | 73 |
同花K | 97 | 95 | 93 | 90 | 88 | 85 | 83 | 81 | 79 |
同花A | 98 | 97 | 96 | 94 | 93 | 91 | 90 | 89 | 87 |
同花顺 | 99 | 99 | 99 | 98 | 98 | 97 | 97 | 96 | 96 |
豹子5 | 99 | 99 | 99 | 99 | 99 | 99 | 99 | 98 | 98 |
豹子A | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
每种情况与理论分析的误差不大于一个万分点。
打个比方撒,桌面是五人局,你的牌中有一对A,那么你的胜率就是69%左右,牌越大肯定胜率越高,随着桌上人数的增加,同样的牌,胜率逐步降低的。
我们假定胜率大于50%为大牌,可得下表:
人数 | 大牌标准 |
---|---|
2人 | 杂牌A |
3人 | 对2 |
4人 | 对6 |
5人 | 对10 |
6人 | 对Q |
7人 | 对K |
8人 | 对A |
9人 | 顺456 |
10人 | 顺789 |
解析:
1.牌型概率的理论计算
从 52 张牌中任取 3 张牌,总共可能出现的情况为 C352 = 22100种
- 豹子
总共 13 ∗ 4 = 52 种,概率 P1= 0.235 % - 顺金
枚举,A23, 234, …, QKA
总共 12 ∗ 4 = 48 种,概率 P2= 0.217 % - 金花
先考虑黑桃金花,即从黑桃牌共计 13 张牌中抽取 3 张,然后减去黑桃顺金部分,最后乘 4 即可:
总共 (C313 -12) ∗ 4 = 1096 种,概率 P3 = 4.959 % - 顺子
先考虑 A,2,3 顺,共 43= 64 种,除去A, 2, 3顺金部分,最后乘 12 即可:
总共 ( 43− 4 ) ∗ 12 = 720种,概率 P4 = 3.258 % - 对子
先考虑 A 对,共 C24 ∗ ( 12 ∗ 4 ) = 288种,最后乘 13 即可:
总共 C24 ∗ ( 12 ∗ 4 ) ∗ 13 = 3744种,概率 P5= 16.941 % - 散牌
剩下的概率都是拿到散牌,约为75% 。散牌的种类不可胜数,一个恰当的统计方法是以散牌中最大的那张牌作为牌种区分的标志。比如,散牌 A,表示当前手牌为散牌,且3张牌中最大的牌为 A。
散 A
先考虑黑桃散A,即,现在已经确定手上有一张黑桃 A,那么剩下的两张牌不能:
I. 自成对,例如 两个K,两个Q等,该情况总共有C24 ∗ 12 = 72 种
II. 与黑桃 A 成顺,即 A23 或 QKA,该情况总共有 4 ∗ 4 ∗ 2 = 32种
III. 与黑桃 A 成花,该情况总共有 C212 = 66种
此外,II 与 III 具有重叠部分,即顺金A23, QKA 两种,需要额外补偿回来。
因此黑桃散A 的总可能情况应该如此描述, 另外两张牌应该从 2 - K 中去取,并且不能I.自成对,II与黑桃A成顺,也不能III.与黑桃A成花。计算如下:
C248 − C24 ∗ 12 − 4 ∗ 4 ∗ 2 − C212 + 2 = 960 ,所有散A的种数为上述结果乘 4,即 3840 种。
因此,概率 PA= 17.376%
散 K
类似于散 A 的计算。但是,根据定义,散 K 里一定没有 A,否则它便是散 A。故在计算时应该直接从除去 4 张 A 的牌堆里抽取。计算如下:4 ∗ ( C244 − C24 ∗ 11 − 4 ∗ 4 − C211 + 1 ) = 3240。
因此,概率 PK = 14.661%
散 Q
总计 2640 种,概率 PQ=11.946%
散 J
总计 2100 种,概率 PJ= 9.502%
散 10
总计 1620 种,概率 P10= 7.330%
散 9
总计 1200 种,概率 P9= 5.430 %
散 8
总计 840 种,概率 P8= 3.801 %
散 7
总计 540 种,概率 P7= 2.443 %
散 6
总计 300 种,概率 P6= 1.357 %
散 5
总计 120 种,概率 P5= 0.543 %
不存在散4及其以下,即不可能存在当前牌为散牌,且最高牌不大于4的情况。
2. 利用python3模拟
除了理论计算的方法,还可以尝试使用编程模拟解决。这里考虑用的是python3进行模拟。要点在于如何判别牌型:
先对3张手牌按数字大小以降序排序
若三张牌点数相同,判为豹子,否则步入步骤3
若三张牌点数成公差 -1 的等差数列,进一步判断,否则步入步骤4
3.1 若三张牌花色一致,判为顺金
3.2 否则,判为顺子
若三张牌花色一致,判为金花,否则步入步骤5
若第一张牌与二张牌点数相同,或者第二张牌与第三张牌点数相同,判为对子,否则步入步骤6
其余情况,判为散 x,其中 x 是第一张牌的点数
在样本总容量取 107
情况下,得到结果如下:
3. 每种牌型的胜率
这里只考虑庄,闲玩法,而不深究多人玩法(概率随人数而变化)。在计算胜率时,理论分析存在很大困难,这是因为对于特定牌型的胜率考察,涉及到条件概率,需要讨论的情况繁多。因此,采用计算机进行模拟以得到一个近似值。要点在于如何比较两副手牌的大小:
首先判断牌型,按照 豹子 > 顺金 > 金花 > 顺子 > 顺子 > 散牌 的规则进行第一次比较,如果牌型相同,步入步骤2
如果是对子,则先比较对子大小,如果相同,再比较剩下的那张单牌的大小
否则,分别对两副手牌排序,按顺序比较即可
同样在样本总容量取 107
情况下,得到胜率的模拟结果如下:
4. 总结
最后提及很有趣的一点。虽然 豹子 > 顺金,但是豹子出现的概率却略大于顺金;同样,金花 > 顺子,但是金花出现的概率要比顺子大。
附python3代码:
# -*- coding: utf-8 -*-
import random
import matplotlib.pyplot as plot
import numpy
color_book = { 1: "", 2: "", 3: "", 4: ""}
num_book = { "J": 11, 11: "J", "Q": 12, 12: "Q", "K": 13, 13: "K", "A": 14, 14: "A"}
type_book = { 100: "豹子", 101: "顺金", 102: "金花", 103: "顺子", 104: "对子",
14: "散A", 13: "散K", 12: "散Q", 11: "散J", 10: "散10", 9: "散9", 8: "散8", 7: "散7", 6: "散6", 5: "散5"}
lvl_book = [100, 101, 102, 103, 104, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5]
card_book = list()
'''单张牌'''
class Card:
def __init__(self, num, color = 1):
self.num = num
self.color = color
'''手牌'''
class Hand:
def __init__(self, cardList):
self.hand = sorted(cardList, key=lambda x: x.num, reverse=True)
def parseType(self) -> int:
#100. 豹子
if self.hand[0].num == self.hand[1].num and self.hand[0].num == self.hand[2].num:
return 100
# 金花 & 顺金
elif self.hand[0].color == self.hand[1].color and self.hand[0].color == self.hand[2].color:
# 101. 顺金
if (self.hand[2].num + 1 == self.hand[1].num and self.hand[1].num + 1 == self.hand[0].num \
or self.hand[2].num == 2 and self.hand[1].num == 3 and self.hand[0].num == 14):
# 因为 A 是以数字 14 存储的,因此 A23 顺要额外考虑
return 101
# 102. 金花
else:
return 102
# 103 顺子
# 因为上个 if 已经除去了顺金部分,因此只要是公差为1的等差数列则一定是顺子
elif self.hand[2].num + 1 == self.hand[1].num and self.hand[1].num + 1 == self.hand[0].num \
or self.hand[2].num == 2 and self.hand[1].num == 3 and self.hand[0].num == 14:
return 103
# 104. 对子, 对子要额外记录一下对子和单牌,以便比较大小
elif self.hand[2].num == self.hand[1].num or self.hand[1].num == self.hand[0].num:
if self.hand[2].num == self.hand[1].num:
self.pair, self.single = self.hand[2].num, self.hand[0].num
else:
self.pair, self.single = self.hand[0].num, self.hand[2].num
return 104
# 散牌
else:
return self.hand[0].num
'''初始化牌堆'''
def initCard():
# 1 表示 A
# 13 表示 K
for i in range(2, 15):
for j in range(1, 5):
card_book.append(Card(i, j))
'''打印手牌'''
def printHand(handCard: Hand):
for h in handCard.hand:
print(h.num, end='') if 1 < h.num <= 10 else print(num_book[h.num], end='')
print(color_book[h.color], end=' ')
print(type_book[handCard.parseType()])
'''比较辅助函数'''
def cmpCard(h1: Hand, h2: Hand):
for i in range(len(h1.hand)):
if h1.hand[i].num != h2.hand[i].num:
if h1.hand[i].num > h2.hand[i].num:
return 0
else:
return 2
return 1
'''比较手牌函数'''
def cmpHand(h1: Hand, h2: Hand):
lvl1 = h1.parseType()
lvl2 = h2.parseType()
#printHand(h1)
#printHand(h2)
# 先比较牌种
if lvl_book.index(lvl1) < lvl_book.index(lvl2):
return 0
elif lvl_book.index(lvl1) > lvl_book.index(lvl2):
return 2
# 对子,要先比对子
elif lvl1 == 104:
if h1.pair == h2.pair:
return cmpCard(Hand([Card(h1.single)]), Hand([Card(h2.single)]))
else:
return cmpCard(Hand([Card(h1.pair)]), Hand([Card(h2.pair)]))
# 其余情况,挨个比就行
else:
return cmpCard(h1, h2)
'''作图函数'''
def plotRects(x_list, y_list):
rects = plot.bar(range(len(y_list)), y_list, color=[numpy.random.random(3) for i in range(len(y_list))])
plot.ylabel("概率(%)")
plot.xticks([i for i in range(len(x_list))], x_list)
for rect in rects:
height = rect.get_height()
plot.text(rect.get_x() + rect.get_width() / 2, height, "{:.3f}%".format(height), ha='center', va='bottom')
plot.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plot.rcParams['axes.unicode_minus'] = False
plot.show()
'''获得全部牌型的概率'''
def getPr(cap: int):
cnt = dict()
tot = int(cap)
for i in range(tot):
hand1 = Hand(random.sample(card_book, 3))
type = hand1.parseType()
cnt[hand1.parseType()] = 1 if type not in cnt else cnt[hand1.parseType()] + 1
cnt = dict(sorted(cnt.items(), key=lambda x: x[1]))
'''分组画图'''
sub1 = dict([(key, cnt[key]) for key in range(100, 105)])
sub2 = dict([(key, cnt[key]) for key in range(5, 15)])
name_list1 = [type_book[k] for k in sub1.keys()]
val_list1 = [v / tot * 100 for v in sub1.values()]
name_list2 = [type_book[k] for k in sub2.keys()]
val_list2 = [v / tot * 100 for v in sub2.values()]
plotRects(name_list1, val_list1)
plotRects(name_list2, val_list2)
'''获得全部牌型的胜率'''
def getWinning(cap: int):
result = { }
tot = int(cap)
for i in range(tot):
hand = random.sample(card_book, 6)
hand1 = Hand(hand[:3])
hand2 = Hand(hand[-3:])
type = hand1.parseType()
if type in result.keys():
# print(type, cmpHand(hand1, hand2))
result[type][cmpHand(hand1, hand2)] += 1
else:
result[type] = [0] * 3
result[type][cmpHand(hand1, hand2)] = 1
'''分组画图'''
sub1 = dict([(key, result[key]) for key in range(100, 105)])
sub2 = dict([(key, result[key]) for key in range(5, 15)])
name_list1 = [type_book[k] for k in sub1.keys()]
val_list1 = [v[0] / sum(v) * 100 for v in sub1.values()]
name_list2 = [type_book[k] for k in sub2.keys()]
val_list2 = [v[0] / sum(v) * 100 for v in sub2.values()]
plotRects(name_list1, val_list1)
plotRects(name_list2, val_list2)
if __name__ == '__main__':
initCard()
#getPr(1e7)
getWinning(1e7)
GitHub地址:github.com/Jiangzemin1926/Goldflower
温馨提示:假期在家,适当怡情,不要上头哦~~
最后,不要忘了或支持一下哦