平常在外漂泊,每天都是作業、作業、作業!不會接觸到打牌娛樂啥的,可是回家過年的時候,難免會和親戚朋友聚聚打打牌啥的,想要少輸點不得從網上找找攻略啊,這不,在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
溫馨提示:假期在家,適當怡情,不要上頭哦~~
最后,不要忘了?或📑支持一下哦
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/263346.html
標籤:其他
