反直覺的一個游戲 - 三門問題 (Monty Hall problem)


三門問題,也叫蒙提霍爾問題(Monty Hall Problem)

以電視節目 - Let's make a deal的主持人蒙提霍命名的一個反直覺問題。

游戲簡介

假設有3個門。 其中一個后面藏着寶藏,其余2個都是空門(美國節目中1個后面有汽車,其余的2個是山羊)。 

游戲開始:

首先你先選擇一張門,

選好后,主持人幫你在其余2扇沒有被選擇的門中打開一扇沒獎的門。

 

這時候你有一個選擇權, 換門還是不換門?

直覺上換不換幾率都是50%,那到底幾率是多少? 換是不是得獎的機會大一些?

可以用代碼來模擬一遍。python

編程就是一步一步來,當然有時候要想清楚直接到達想要到的地方。

第一遍我們就用一步步來的方法走一遍吧。 (代碼一部分借鑒了哈佛的CS109)

首先我們先定義下 有3扇門,其中獎品在某一扇門中。 

 

首先安裝下 numpy庫

1.這個可以用 random.randint來實現。 randint(start, end, size) 隨機選擇的3個門中間的一個

1 def simulate_prizedoor(nsim):
2     return np.random.randint(0, 3, (nsim))

2.然后我們可以定義下,一開始選擇的一扇門。 這里可以用固定選擇法,也可以用隨機法,數據大的情況下差別不大。這里我們直接用了固定選擇第一扇門(0)

1 def simulate_guess(nsim):
2     return np.zeros(nsim, dtype=np.int)

3.然后我們 模擬主持人,開一扇沒有獎品的門。

1 def goat_door(prizedoors, guesses):
2     result = np.random.randint(0, 3, prizedoors.size)
3     while True:
4         bad = (result == prizedoors) | (result == guesses)
5         if not bad.any():
6             return result
7         result[bad] = np.random.randint(0, 3, bad.sum())

這里要實現的邏輯是, 先生成一個0到2的隨機數。 然后匹配直到 不等於 我們一開始的選擇的那扇門 或 寶藏存在的那扇門。 

 

4.然后模擬,我們假如我們在主持人打開門之后, 選擇換一張門。

1 def switch_guess(guesses, goatdoors):
2     result = np.random.randint(0, 3, guesses.size)
3     while True:
4         bad = (result == guesses) | (result == goatdoors)
5         if not bad.any():
6             return result
7         result[bad] = np.random.randint(0, 3, bad.sum())

看得出來實現的代碼和上面是一樣的。 我們換的這扇門,不能是原來那扇,而且也不能是開了的那扇。

5.計算勝率。

1 def win_percentage(guesses, prizedoors):
2     return 100 * (guesses == prizedoors).mean()

這里需要注意, 如果單純的bool type的數據是沒有mean這個函數的。 np 把真假轉換成了1,0這樣才可以計算平均數。mean = 總值/數組的總數

 

6.最后我們就可以測試下,換和不換的區別了。 因為大數定律,我們測試100000遍

 1 pd = simulate_prizedoor(nsim)
 2 guess = simulate_guess(nsim)
 3 goats = goat_door(pd, guess)
 4 guess = switch_guess(guess, goats)
 5 
 6 nsim = 100000
 7 
 8 print "Win percentage when keeping original door"
 9 print win_percentage(simulate_prizedoor(nsim), simulate_guess(nsim))
10 
11 print "Win percentage when switching doors"
12 print win_percentage(pd, guess).mean()
Win percentage when keeping original door
33.32
Win percentage when switching doors
66.69

測試結果, 勝率方面 換能提高1倍的勝率。 (2個數相加超過100是因為小數點后的換算原因。)

測試完如果有點不能接受,我們換種方式

 

————————————————————————————一百遍的分割線——————————————————————————————————————

 

一切都不變的情況下,假如說有100扇門,你先選一扇,主持人幫你開98扇, 這時候也只剩下了2扇門。 你是換呢,還是不換。

這個代碼實現就有點小區別了。 因為大家熟悉了之前的實現方法, 我就換了一種實現方法。

1. 定義100扇門中有1個寶箱,這個做太多改變, 再定義我們隨機選擇了一扇門(之前是固定)

1 start = 0
2 end = 100
3 def simulate_prizedoor(nsim):
4     return np.random.randint(start, end, (nsim))
5
6 def simulate_guess(nsim):
7     return np.random.randint(start, end, (nsim))

2. 這里我們的主持人要關98扇門,真正要實現這個這個模擬,需要各種循環和條件。 其實根本沒必要那么麻煩, 實現一個程序或者過程只要目的達到了就OK,不管是不是一模一樣的模擬了一遍。  編程就是要跳出盒子,什么事都在盒子里面,永遠無法做到效率的解決問題。

實現邏輯: 這里面不管主持人開多少門, 最后只會留2扇門。 這2扇門只有2種可能性:以猜中沒猜中為邏輯

1. 我們猜中了, 所以我們猜的門 和 另外一扇隨機留下的門

2. 我們猜錯了,所以留下一扇我們猜的門,和一扇寶藏門。

實現這個代碼很簡單。

1 def guess_switch(guess_keep, pd): 
2     alter = np.random.choice([x for x in range(start,end) if ((x != guess_keep)&(x != pd)).any()])
3     num = np.where(guess_keep == pd,alter,pd)
4     return num

 

3. 勝率計算

 1 def win_percentage(guesses, prizedoors):
 2     return 100*(guesses == prizedoors).mean()
 3 
 4 nsim = 10000
 5 
 6 pd = simulate_prizedoor(nsim)
 7 guess_keep = simulate_guess(nsim)
 8 guess_sh = guess_switch(guess_keep, pd)
 9 
10 #keep guesses
11 print "Win percentage when keeping original door"
12 print win_percentage(pd, guess_keep)
13 
14 #switch
15 print "Win percentage when switching doors"
16 print win_percentage(pd, guess_sh)
Win percentage when keeping original door
1.04
Win percentage when switching doors
98.96

數據勝於雄辯。

PS:本人代碼水平有限, 請大家多多指正。 另外一個問題, 有沒有可能實現所有的if都用np.where來替代。 if和for真是效率太低了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM