在課前預習的講義中,老師向大家介紹了著名的Monty Hall Problem,並以條件機率以及貝氏定理的角度詮釋了「資訊流入」前後的差別。
如果還是感到很confused,不妨到以下網站來模擬一下,自己玩玩看這個Monty Hall Problem!
當然,到了電腦實習課,我們應該要有能力自己寫出模擬的程式!畢竟上面的網站,其背後的程式是怎麼寫的,我們怎麼會知道呢,當然不能不求甚解啦!
首先我們來定義一個function
#Monty Hall (Original) #把問題寫成換與不換為input,有沒有得到car為output的fcn monty_hall = function(switch = logical()){ #randomly arrange the door doors = c(1,2,3) names(doors) = sample(c("goat", "car", "goat")) prize_door = which(names(doors)=='car') #find the index of car #Now Guess guess = sample(doors, 1) if(guess == prize_door){ revealed_door = sample(doors[doors != prize_door], 1) #randomly open one of the goat door }else{ revealed_door = doors[doors != prize_door & doors != guess] #open the other goat door } #Show the return if(switch){ switched_door = doors[doors != guess & doors != revealed_door] return(prize_door == switched_door) } else { return(prize_door == guess) } }
程式碼解說
函式所需的引數是「換」或「不換」,因為我們好奇的是「在一扇背後沒有車的門被主持人打開後,我們選擇『換』後贏得車的機率是否會變高」,所以把「換/不換」這個選項提到最前面是沒有關係的。實際上,我們就是在比較:(1.) 堅持不換 及 (2.)無論如何堅持要換,兩種策略讓我們贏得車的機率有何差別。
程式的第一個部分是隨機分配對應獎項的門,接著隨機地猜一道門。到這裡都相當直觀,每一道門被指派為獎品是車的機率是均等的;我們選到車的機率也是均等的。
door
是一個有名字的向量(named vector),我們可以很簡單地用中括號([]
)來取向量的子集合。這是R的原生結構,跟python裡的pandas很像。
接著是一些邏輯判斷,因為主持人所開啟的門會依據我們選的門背後是不是車而有變化。如果我們選到的門背後是車,那麼主持人在剩下的兩道門中隨機選擇打開一道:
if(guess == prize_door){ revealed_door = sample(doors[doors != prize_door], 1) }
如果我們選到的門背後是山羊,那麼主持人只有剩下另一道山羊門可以打開:
else{ revealed_door = doors[doors != prize_door & doors != guess] }
選擇完要不要換(也就是我們的input,或者說引數argument所決定的)以後,答案就呼之欲出了!
假設我們選擇換:
那麼這時選擇的門會是哪一道呢?不會是我們一開始選的那道,也不會是被主持人打開的那道,如此一來用R最基本的邏輯判斷寫法,就可以知道如何「篩選」出正確的門了:
if(switch){ switched_door = doors[doors != guess & doors != revealed_door] return(prize_door == switched_door) }
switched_door
表示決定換之後要選擇的門,這扇門必須從三扇門透過排除法得到。
guess
是我們最一開始隨機猜的那道門,所以最終選擇的門 switched_door
不會是它。revealed_door
則是主持人打開的門,我們也不能選它。我們利用取子集合的方式來「篩」doors
這個向量,它既然不是上述兩道門,所以取交集,以得到switched_door
。最後把這道最終選擇之門跟藏有汽車的門比對即可輸出結果。
假設我們選擇不換:
那答案就很簡單了,只要把最一開始猜的 guess
跟藏有汽車的門比對即可輸出結果:
return(prize_door == guess)
看看模擬的結果如何
直接執行 monty_hall(T)
或monty_hall(F)
就可以看到玩「一次」Monty Hall Problem的結果囉!輸出如果是1表示你得到車了,若是0則是沒有。
那麼現在只要執行非常多變以上兩種策略的結果,然後比較看看得到車的機率有沒有差別就可以了!
# 選擇換 mean(replicate(monty_hall(T), n = 10000)) # 選擇不換 mean(replicate(monty_hall(F), n = 10000))
視覺化
result = replicate(monty_hall(T), n = 10000) barplot(c(sum(result), length(result)-sum(result)), main = 'The frequence of winning the car in Monty Hall Problem in 10000 rounds', names.arg = c('Change', 'Do not change'), ylim = c(0,10000))
結果是不是顯而易見呢?
拓展到n扇門的版本
事實上,要將這個程式碼改寫成n扇門的版本也相當容易!
monty_hall_n = function(switch = logical(), n){ #randomly arrange the door doors = 1:n names(doors) = sample(rep(c("goat", "car"), c(n-1,1))) prize_door = which(names(doors)=='car') #find the index of car #Now Guess guess = sample(doors, 1) if(guess == prize_door){ revealed_door = sample(doors[doors != prize_door], n-2) #randomly open one of the goat door }else{ #revealed_door = sample(doors[doors != prize_door & doors != guess], n-2) revealed_door = doors[!doors %in% c(prize_door, guess)] #open the other goat door } #Show the return if(switch){ switched_door = doors[!doors %in% c(guess, revealed_door)]#'%in%' is value matching return(prize_door == switched_door) } else { return(prize_door == guess) } }
請參考
更詳盡的程式碼文檔以及解說請參考: