揭開臺灣麻將的神秘面紗!臺灣麻將天聽機率到底有多高?

在台灣,麻將應該很多人都打過,但有誰天胡過呢?
可能有大大有遇過,但至少本貓我是沒遇過啦 ┐(´д`)┌
為了證明我不是非洲人,所以我就來算算看天胡的機率。
排列組合
在遊戲剛開始,大家會拿到 16 張牌,莊家開門後手上會有 17 張牌。
這時如果莊家胡了,那就稱作天胡。
所以我們要算的就是直接從麻將牌堆裡面隨機拿 17 張可以直接胡牌的機率。
這時候是不是有人說:“這簡單,我只要將從牌堆裡面拿 17 張的所有可能找出來,並檢查看看是否胡牌,統計一下就能知道機率了”。

想法很好,但計算太久了,你算看看,麻將包含 4 種花色,萬筒條共有 $9\times4\times3$ 個,字牌有 $7\times4$ 個,一共有 136 張。
算上排列組合之後共有 $\dbinom{136}{17} = 1,845,382,436,487,682,488,000$ 種拿取的方法 (同樣的牌都當做不同)。
這簡直是天文數字,不知道要算多久,因此,我們既然沒辦法列出所有可能,但我們換個方向,是不是可以找出所有胡牌手牌?
胡牌
大家都知道,台灣麻將胡牌的時候要有 5 組面子 (刻子或順子) 加上 1 個對子 (眼睛),像下圖這樣:

















面子:順子或是刻子
- 順子:數字連續三張同花色的牌



- 刻子:三張相同的牌



- 對子:兩張相同的牌


我們可以注意到 5 組面子和 1 個對子可能落在萬筒條字中任何一個。
單看一種花色的話,其實有 12 種狀態:
- 0 個對子
- 0 個面子
- 1 個面子
- 2 個面子
- 3 個面子
- 4 個面子
- 5 個面子
- 1 個對子
- 0 個面子
- 1 個面子
- 2 個面子
- 3 個面子
- 4 個面子
- 5 個面子
大致上來說就是 0 ~ 5 個面子再算上有沒有對子。
萬筒條字每一種花色都有這 12 種狀態,透過組合,就能形成一組胡牌手牌了。
例如:我們萬拿 1 組面子,筒也拿 1 組面子,條拿 3 組面子,字拿 1 個對子,這樣共有 5 組面子和 1 個對子,可以胡牌。
也可以萬拿 3 組面子和 1 組對子,筒也拿 1 組面子,條拿 1 組面子,這樣也有 5 組面子和 1 個對子。
列出其中幾種組合:
萬 | 筒 | 條 | 字 |
---|---|---|---|
1 面 | 1 面 | 3 面 | 1 對 |
3 面 + 1 對 | 1 面 | 1 面 | - |
2 面 | 1 面 + 1 對 | 1 面 | 1 面 |
4 面 | - | 1 面 + 1 對 | - |
- | - | - | 5 面 + 1 對 |
到這裡,你是不是發現到,如果我們能知道這每一種組合類型的胡牌手牌有多少種,那全部加起來就是我們所要找的胡牌手牌個數!!!

列舉
如果說,用上面的例子,萬拿 1 組面子,筒也拿 1 組面子,條拿 3 組面子,字拿 1 個對子。
我分別告訴你萬筒條字在各自的條件下,有多少個可能,相信大家都能知道那個例子的條件下共有多少胡牌手牌,沒錯,全部相乘起來。
假設萬 1 組面子的可能共有 a 個,筒 1 組面子的可能有 b 個,條拿 3 組面子的可能有 c 個,字拿 1 個對子的可能有 d 個,那該例子的胡牌手牌就是 $a\times b\times c \times d$。
至於計算萬拿 1 組面子的可能的方式,我們就直接列舉所有可能,並計算目標個數。
⚠️ 注意:由於我們將所有牌當作不同張牌,因此我們在計算的時候要考慮到選擇的問題。
例如:345 萬,3 萬可以從 4 張裡面選擇 1 張,4 萬和 5 萬也是,這樣共有 $4^3$ 種不同的組合,雖然都是 345 萬,但是因為用了不同的 3 萬、4 萬和 5 萬,因此被當作不同的組合。



如果用 Python 寫起來的話,大概長下方這樣。
# 萬有 1 萬到 9 萬,每個共有 0 張到 4 張
numEachTile = [list(range(5)) for i in range(9)]
allPossibleHands = list(itertools.product(*numEachTile))
# 對於每種萬的手牌
for hand in allPossibleHands:
# 由於我們只拿一組,因此就拿 3 張
if sum(comb) != 3: continue
# 雖然是 3 張,但也要確認是不是面子
if checkSets(list(hand)):
# 找到符合條件的手牌
found += permutation(list(hand)) # 紀錄次數
其中 permutation 的部分只要用 $\dbinom{4}{k}$ 計算即可,k 為同樣張牌的個數。
# 計算 n 個裡面取 r 個的種類
def ncr(n, r):
r = min(r, n-r)
numer = reduce(op.mul, range(n, n-r, -1), 1)
denom = reduce(op.mul, range(1, r+1), 1)
return numer // denom
# 有多少種不同的選擇
NCR = [ncr(4, i) for i in range(5)]
def prob(hand):
p = 1
for i in range(len(hand)):
p *= NCR[hand[i]]
return p
以下列出其中幾個找出來符合條件的結果:









如果說要找 1 組以上,那就調整一下參數即可。
使用上方的方法,我們可以計算出特定一種花色下,符合面子條件的個數,並分別計算 a, b, c。
至於計算字牌 1 組對子的方式只要調整一下就可以了:
# 字牌有 7 種,每個共有 0 張到 4 張
numEachTile = [list(range(5)) for i in range(7)]
allPossibleHands = list(itertools.product(*numEachTile))
# 對於每種字牌的手牌
for hand in allPossibleHands:
# 由於我們要 1 組對子,因此拿 2 張
if sum(comb) != 2: continue
# 確認是不是對子
if checkPair(list(hand)):
# 找到符合條件的手牌
found += permutation(list(hand)) # 紀錄次數
那在計算出 a, b, c, d 之後,我們就可以全部相乘起來,找到在 “萬拿 1 組面子,筒也拿 1 組面子,條拿 3 組面子,字拿 1 個對子” 的狀況下的胡牌手牌個數
判斷面子和對子
上面提到的方法,看起來沒問題,寫起來不難。
但如果我今天要找萬有 2 組面子,怎麼樣才能有效率地判斷呢?
更甚至,如果今天萬有 5 組面子和 1 組對子,那到底要怎麼判斷才快呢?
在打麻將的時候,其實就常常遇到這個問題吧?

這篇 paper1 裡有提到快速判斷胡牌的方法,我們可以拿來判斷問題。
單純只有面子
我們先考慮沒有對子的時候。
萬筒條
我們從數字 1 開始檢查。
- 如果這個數字數量 $\geq 3$ 個,那我們直接拿走一組刻子 (三張一樣的)。
- 如果數量 $\geq 0$ 個,但是不滿 3 個,那我們判斷順子,也就是向後看兩個,判斷是否有順子,有就拿走,沒有就代表不符合條件。
- 如果數量 $= 0$ 個,我們就換下個數字檢查。
因為我們只有剛好足夠組成條件的牌,例如萬要 1 組面子,我們手上就只有 3 張,要有多的牌,那一定就組不成了。
舉例如果現在要判斷是否為 2 組面子,手牌呈現如下:






從數字 1 開始,由於有 4 張,我們首先拿走 3 張,剩下這些:



由於數字 1 只剩下 1 張,因此檢查順子,剛好有 1 組順子,於是就拿走,剛好沒了。
由於後面都沒有了,就一直跳下一個數字,直到數字 9 結束後都沒有錯誤,該手牌即為我們要找的。
我們再看一組錯誤的,現在要判斷是否為 3 組面子,手牌如下:









從數字 1 開始,因為沒有,換看數字 2。
由於數字 2 不到 3 張,因此看順子,由於數字 2 可以湊成一組順子,剩下:






可以看到數字 2 和數字 3 湊不成順子,因此這手牌不含有 3 組面子。
字牌
萬筒條結束了,那字怎麼辦?
字由於沒有順子,因此簡單的多,就直接看各數字張數。
- 數量為 0 或是 3:符合條件。
- 數量為 1 或是 2:不符合條件。
由於字牌只能組成刻子,因此不是 0 張就是 3 張。
像是下列手牌就不是我們要找的字含有 1 組面子:



因為東和西分別為 2 張和 1 張,不是 0 或是 3。
面子 + 對子
那如果我們考慮更複雜的情況,今天要判斷 5 組面子和 1 組對子。
其實換個想法,既然對子只有一組,那我們假設所有對子的情況,然後將對子剔除,剩下的跑只有面子的判斷方法不就好了嗎!!!
用這個方式,假設對子的情況只要有一個判斷成功,那就是成功了。
舉例,今天要找 2 組面子和 1 組對子。








由於對子為兩張,因此只要假設數字有超過 2 張的就好了。
- 假設 1:
為對子,剩下
- 明顯剩下的沒有 2 組面子。
- 假設 2:
為對子,剩下
- 剩下剛好
一組,
為一組。
雖然假設 1 錯誤,但假設 2 通過,所以手牌符合條件。
結果
好啦,說了那麼多方法,那到底本貓我到底是不是非洲人?
台灣麻將一共有 1,554,490,175,889,888 組胡牌手牌。
如果算上機率的話 (除以 $\dbinom{136}{17}$) 大約為 0.0000008424 ($\frac{1}{1,187,130}$)。
換句話說就是 119 萬次才會出現一次,我想我沒有打過那麼多次的麻將… 所以我不是非洲人!!!

順帶一提,大樂透的頭獎機率要 1400 萬次才會中一次。
恩…….我已經兩年沒有中發票了,我還是先不要嘗試好了。
Reference
- 本篇麻將牌都由 FluffyStuff 製作的麻將牌圖片 Link
如果你覺得這篇文章有用 可以考慮贊助飲料給大貓咪