史上最强最全“炸金花”攻略(涉及概率论及排列组合+py3模拟)

   日期:2021-02-25     浏览:154    评论:0    
核心提示:平常在外漂泊,每天都是工作、工作、工作!不会接触到打牌娱乐啥的,可是回家过年的时候,难免会和亲戚朋友聚聚打打牌啥的。想要少输点不得从网上找找攻略啊,这不,在GitHub上就有,嘿嘿~~Github上面这个项目模拟了2000多万局炸金花,计算了不同牌在不同玩家数下的炸金花胜率,并且绘制了一个胜率表:2人3人4人5人6人7人8人9人10人杂牌7200000000杂牌101310000000杂牌Q31931000

平常在外漂泊,每天都是工作、工作、工作!不会接触到打牌娱乐啥的,可是回家过年的时候,难免会和亲戚朋友聚聚打打牌啥的。想要少输点不得从网上找找攻略啊,这不,在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

温馨提示:假期在家,适当怡情,不要上头哦~~

最后,不要忘了或支持一下哦

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服