最初看到這個問題是初中的時候買了一本有關數學謎題的書里面概率論的一張的課后拓展就是說到三門問題,當時作為一個擴展閱讀看了一下,里面說到了一個世界智商最高的女人秒殺了美國一大群的數學高材生的精彩故事(比較夸張),當時對這個問題也是似懂非懂。
什么是蒙提霍爾問題?

蒙提霍爾問題,亦稱為蒙特霍問題或三門問題(英文:Monty Hall problem),是一個源自博弈論的數學游戲問題,大致出自美國的電視游戲節目Let's Make a Deal。問題的名字來自該節目的主持人蒙提·霍爾(Monty Hall)。
最初的表述是:
參賽者會看見三扇關閉了的門,其中一扇的后面有一輛汽車,選中后面有車的那扇門就可以贏得該汽車,而另外兩扇門后面則各藏有一只山羊。當參賽者選定了一扇門,但未去開啟它的時候,節目主持人開啟剩下兩扇門的其中一扇,露出其中一只山羊。主持人其后會問參賽者要不要換另一扇仍然關上的門。
問題是:換另一扇門會否增加參賽者贏得汽車的機會率?
這個古老的問題一經提出就引起了劇烈的爭論,有人認為換與不換最終得到車的概率都是1/2,有人認為換門之后得到車的概率更大,應該選擇換門之后得到車的概率為2/3在撰寫這篇文章的時候在果殼上還有人在為此爭吵,知乎上也有許多關于這方面的討論,其實這些爭論很多情況下都是因這個問題的模糊表述所引起的,關鍵點在于主持人對于門后的情況是否了解:
- 如果主持人事先知道哪個門里有山羊并且他特意選擇了有山羊的門打開了,那么參賽者應該換另一扇門,這可以將他勝利的概率從1/3升到2/3
- 如果主持人事先不知道哪個門里有山羊或者他只是隨機的選擇了一個門,但事實發現里面恰好是山羊。這時候參賽者沒有換門的必要,勝利概率總是1/2
為了后續的討論,這里采用維基百科上對于這一個問題的不含糊的定義
嚴格的表述如下:
- 參賽者在三扇門中挑選一扇。他并不知道內里有什么。
- 主持人知道每扇門后面有什么。
- 主持人必須開啟剩下的其中一扇門,并且必須提供換門的機會。
- 主持人永遠都會挑一扇有山羊的門。
- 如果參賽者挑了一扇有山羊的門,主持人必須挑另一扇有山羊的門。
- 如果參賽者挑了一扇有汽車的門,主持人隨機在另外兩扇門中挑一扇有山羊的門。
- 參賽者會被問是否保持他的原來選擇,還是轉而選擇剩下的那一道門。
那么這個問題這可以很好的理解了,引用維基的一幅圖片解析:

有三種可能的情況,全部都有相等的可能性(1/3):
- 參賽者挑汽車,主持人挑兩頭羊的任何一頭。轉換將失敗。
- 參賽者挑A羊,主持人挑B羊。轉換將贏得汽車。
- 參賽者挑B羊,主持人挑A羊。轉換將贏得汽車。
所以玩家選擇換門之后獲勝的概率應為2/3
證明?

定義:
-
事件A
為一開始玩家選擇的一扇門 -
事件H
為最后門后的結果
- 如果是選擇不換門的策略
因為選擇的是不交換的策略,所有只有一開始選中的是汽車,最后才能選中汽車。
- 選擇交換門的策略
因為選擇的是交換的策略,所有只有一開始選中的是羊,最后才能選中汽車。
程序驗證
實踐是檢驗真理的唯一標準,在流言終結者看到他們人工重復這個實驗區驗證,發現這樣很浪費時間。何通過計算機去去模擬這一段過程呢?
下面使用python程序來模擬這一段過程:
from __future__ import division
import logging
from matplotlib import pyplot as plt
import numpy as np
import random
class MontyHall(object):
"""docstring for MontyHall"""
def __init__(self, num=3):
"""
創建一個door列表
0 代表關門
1 表示后面有車
-1 代表門被打開
"""
super(MontyHall, self).__init__()
self.doors = [0] * num
self.doors[0] = 1
self.choice = -1
self.exclude_car = False
self.shuffle()
def shuffle(self):
"""
開始新游戲
重新分配門后的東西
"""
if self.exclude_car == True:
self.doors[0] = 1
self.exclude_car = False
for i in xrange(len(self.doors)):
if self.doors[i] == -1:
self.doors[i] = 0
random.shuffle(self.doors)
def make_choice(self):
"""
player隨機選擇一扇門
"""
self.choice = random.randint(0, len(self.doors) - 1)
logging.info("choice: %d" % self.choice)
logging.info("original: %s" % self.doors)
def exclude_doors(self):
"""
主持人知道門后的情況排除門
直到剩余兩扇門
"""
to_be_excluded = []
for i in xrange(len(self.doors)):
if self.doors[i] == 0 and self.choice != i:
to_be_excluded.append(i)
random.shuffle(to_be_excluded)
for i in xrange(len(self.doors) - 2):
self.doors[to_be_excluded[i]] = -1
logging.info("final: %s" % self.doors)
def random_exclude_doors(self):
"""
主持人并不知道門后面的情況隨機的開門
直到剩余兩扇門
"""
to_be_excluded = []
for i in xrange(len(self.doors)):
if self.doors[i] != -1 and i != self.choice:
to_be_excluded.append(i)
random.shuffle(to_be_excluded)
for i in xrange(len(self.doors) - 2):
if self.doors[to_be_excluded[i]] == 1:
self.exclude_car = True
self.doors[to_be_excluded[i]] = -1
logging.info("final: %s" % self.doors)
def change_choice(self):
"""
player改變選擇
"""
to_change = []
for i in xrange(len(self.doors)):
if self.doors[i] != -1 and i != self.choice:
to_change.append(i)
self.choice = random.choice(to_change)
logging.info("choice changed: %d" % self.choice)
def random_choice(self):
"""
player 第二次隨機選擇門
"""
to_select = []
for i in xrange(len(self.doors)):
if self.doors[i] != -1:
to_select.append(i)
self.choice = random.choice(to_select)
logging.info("random choice : %d" % self.choice)
def show_answer(self):
"""
展示門后的情況
"""
logging.info(self.doors)
def check_result(self):
"""
驗證結果
"""
got_it = False
if self.doors[self.choice] == 1:
got_it = True
return got_it
模擬1000輪,每一輪重復試驗1000次
- 不改變選擇:
def unchange_choice_test(n):
"""
不改變初始的選擇
"""
result = {}
game = MontyHall()
for i in xrange(n):
game.shuffle()
game.make_choice()
game.exclude_doors()
if game.check_result():
result["yes"] = result.get("yes", 0) + 1
else:
result["no"] = result.get("no", 0) + 1
for key in result:
print "%s: %d" % (key, result[key])
return result["yes"] / n
if __name__ == '__main__':
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
results = []
test_num = 1000
round_num = 1000
for x in xrange(0,round_num):
results.append(change_random_test(test_num) )
y_mean = np.mean(results)
y_std = np.std(results)
x = range(0,round_num)
y = results
plt.figure(figsize=(8,4))
plt.xlabel("round")
plt.ylabel("frequency")
plt.title("The frequency of the success")
tx = round_num / 2
ty = y_mean
label_var = "$\sigma \left( X \\right)=$%f" % y_std
label_mean = "$ X =$%f" % y_mean
p1_label = "%s and %s" % (label_var,label_mean)
p1 = plt.plot(x,y,"-",label=p1_label,linewidth=2)
plt.legend(loc='upper left')
pl2 = plt.figure(2)
plt.figure(2)
plt.hist(results,40,normed=1,alpha=0.8)
plt.show()
結果:

概率分布:

成功的概率均值在 1/3 附近
- 改變選擇:
def change_choice_test(n):
"""
交換選擇的門
"""
result = {}
game = MontyHall()
for i in xrange(n):
game.shuffle()
game.make_choice()
game.exclude_doors()
game.change_choice()
if game.check_result():
result["yes"] = result.get("yes", 0) + 1
else:
result["no"] = result.get("no", 0) + 1
for key in result:
print "%s: %d" % (key, result[key])
return result["yes"] / n
同樣的方法繪圖得到結果:

概率分布:

成功的概率均值在 2/3 附近
通過上面的分析與模擬可知最佳的策略當然就是換門。
更加深入的討論
- 如果門的數量不止是3個,如果是50扇門呢?

這種情況下,主持人打開48扇都是羊的門后,再給你選擇,很多人這個時候應該就不會固守那1/2,而會選擇換門
把門的數據增大到100,1000,這種情況會更加明顯。
還是通過一段程序模擬說明:
def change_choice_test_large(n,m):
"""
交換選擇的門
"""
result = {}
game = MontyHall(m)
for i in xrange(n):
game.shuffle()
game.make_choice()
game.exclude_doors()
game.change_choice()
if game.check_result():
result["yes"] = result.get("yes", 0) + 1
else:
result["no"] = result.get("no", 0) + 1
for key in result:
print "%s: %d" % (key, result[key])
return result["yes"] / n
if __name__ == '__main__':
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
results = []
test_num = 1000
round_num = 1000
for x in xrange(0,round_num):
results.append(change_choice_test_large(test_num,50) )
結果:


這時候就要選擇交換門。
- 遇到這種情況我很困惑,我決定拋硬幣決定,這個時候成功的概率?
這是第3種策略,成功的概率和硬幣有關,也就是1/2,這種情況就是從剩下的門中隨機選擇一扇,這個策略從上面分析來看不是最好的,但是比不改變的策略要好。
程序的模擬結果:


- 比如門意外打開的情況呢,也就是上面描述的第二種情況(主持在不知門后的情況下打開門呢)?
這種情況下其實就是一個條件概率,事件A是玩家最后開到的是車,事件B是主持人打開的門是羊。
因為只有主持人開到是羊的情況下,玩家才有可能開到車所以
設玩家第一次選擇的門為事件C
則
- 不交換策略下的條件概率是:
- 交換策略下的條件概率是:
因此在主持人不知道門后的情況下打開一扇,然后發現門后是羊的情況下,換門與不換門最終的概率都是1/2
還是可以通過程序進行模擬:
def unknown_doors_choice_test(n):
"""
主持人并不知道門后面的情況隨機的開門
交換選擇的門
"""
result = {}
game = MontyHall()
continue_count = 0
for i in xrange(n):
game.shuffle()
game.make_choice()
game.random_exclude_doors()
game.change_choice()
if game.exclude_car == False:
continue_count += 1
if game.check_result():
result["yes"] = result.get("yes", 0) + 1
else:
result["no"] = result.get("no", 0) + 1
#for key in result:
# print "%s: %d" % (key, result[key])
logging.info("continue_count: %d" % continue_count)
if continue_count == 0:
return 0.0
return result["yes"] / continue_count


在這種情況下交換門也沒有提升成功的概率
總結
今天寫的這篇東西也算是了解我童年的一個遺憾,人的直覺有時候是很不可靠,要擺脫個人局限的認知才能擁抱更大的世界。
什么?看完這些解析,你還覺得不滿意那么你還可以從下面的參考中尋找更好的解析,本文撰寫過程有部分的圖片引用自一下的參考,如果你還有疑問歡迎你聯系我進一步的討論。
練習
下面是三門問題的兩個翻版,引用自三門問題及相關:
女孩的概率
- 你結交一位新朋友,問她是否有孩子。她說有,有兩個。你問,有女孩嗎?她說有。那么,兩個都是女孩的概率是多少?
答:三分之一。因為生兩個孩子的可能性有四種等可能:BB、GG、BG、GB(即男男、女女、男女、女男)。 因為我們已知至少有一個女兒,所以BB是不可能的。因此GG是可能出現的三個等可能的結果之一,所以兩個孩子都是女兒的概率為三分之一。這對應了三門問題的第一種情況。
- 你結交一位新朋友,問她是否有孩子。她說有,有兩個。你問,有女孩嗎?她說有。第二天,你看見她帶了一個小女孩。你問她,這是你女兒嗎?她說,是。她的兩個孩子都是女孩的概率是多少?
這個概率和生女孩的概率相同,二分之一。這似乎非常奇怪,因為我們所擁有的信息看起來并不比第一種情況時多,但概率卻不同。但是這里的問題其實是,那個你沒>見過的孩子是女孩的概率是多少?這個概率和生女孩的概率相同,二分之一。
這對應了三門問題的第二種情況。當然這里也有語言問題,必須假定這位母親不是特定帶出一個小女孩來給你看的。也就是說你只是碰巧發現了它是位小女孩。這取決于是判斷選擇 或q 隨機選擇。如果是被你碰巧撞見這是屬于隨機選擇。這就對應了三門問題的第二種情況。這其實是增加了信息的。否則如果她主動帶一個小女孩過來給你,則屬于判斷選擇。
你得到的答案依賴于所講的故事;它依賴于你是如何得知至少一個孩子是女孩的。
三囚犯問題
- 亞當、比爾和查爾斯被關在一個監獄里,只有監獄看守知道誰會被判死刑,另外兩位將會獲釋。有1/3的概率會被處死刑的亞當,給他母親寫了一封信,想要獲釋的比爾或查爾斯幫忙代寄。當亞當問看守他應當把他的信交給比爾還是查爾斯時,這位富有同情心的看守很為難。他認為如果他把將要獲釋的人的名字告訴亞當,那么亞當就會有1/2的概率被判死刑,因為剩下的人和亞當這兩人中一定有一個人被處死。如果他隱瞞這信息,亞當被處死的概率是1/3。既然亞當知道其他兩人中必有一人會獲釋,那么亞當自己被處死的概率怎么可能會因為看守告訴他其他兩人中被獲釋者的姓名后而改變呢?
正確的答案是:看守不用當心,因為即使把獲釋人的姓名告訴亞當,亞當被處死的概率仍然是1/3,沒有改變。但是,剩下的那位沒被點名的人就有2/3的概率被處死(被處死的可能性升高了)。如果這個問題換一種說法,就是看守無意間說出了查爾斯不會死。那么概率就會發生改變。
這個其實和三門問題是一致的。你可以把獄卒當成主持人,被處死當成是大獎,那么這個是對應于三門問題的第一種情況,就是主持人知道門后面的情況。獄卒說出誰會被釋放,相當于主持人打開一扇門。但是因為三囚徒問題不能選擇,也就相當于三門問題中的不換門的策略。最終的概率還是1/3是沒有發生改變的。
為了避免產生歧義,規定一下:
1.如果(亞當,查爾斯)被釋放,那么獄卒會告訴亞當:"查爾斯被釋放"。
2.如果(亞當,比爾)被釋放,那么獄卒會告訴亞當:"比爾被釋放"
3.如果(查爾斯,比爾)被釋放,那么獄卒會以1/2的概率告訴亞當:"查爾斯被釋放"或者"比爾被釋放"
意思就很明顯了,在獄卒說出比爾被釋放的條件下,亞當被釋放的概率是?用條件概率算一下。
定義事件:
A :獄卒說出"比爾被釋放"
B :代表亞當被釋放。

那什么時候才是1/2的概率呢?
規則3更改為:如果(查爾斯,比爾)被釋放,那么獄卒會告訴亞當"比爾被釋放"
這個時候計算就是:
![]()
那如果規則3改為:如果(查爾斯,比爾)被釋放,那么獄卒會告訴亞當"查爾斯被釋放"
這個時候:亞當被釋放的概率就會變為1
問題在于規則2和規則3下說"比爾被釋放"不是等概率發生的。
類似的問題還有
- 拋兩枚硬幣其中有一枚硬幣是正面,問兩枚硬幣都是正面的概率是?
- 拋兩枚硬幣其中第一枚硬幣是正面,問兩枚硬幣都是正面的概率是?
the end.
參考:
更新日志:
- 2015-05-20 增加三囚徒問題的解答
- 2015-05-09 第一次撰寫