关于“三门问题(Monty Hall problem)”的仿真与论证

本文通过Python代码实现和理论论证,探讨了蒙提霍尔问题中玩家是否更换选择对赢得汽车概率的影响。仿真结果显示,切换选择可以显著提高获胜率。理论部分解析了概率模型,揭示了直觉与数学概率的不同之处。

关于“三门问题(Monty Hall problem)”的仿真与论证

来自维基百科的介绍:

蒙提霍尔问题(英文:Monty Hall problem),亦称为蒙特霍问题、山羊问题或三门问题,是一个源自博弈论的数学游戏问题,参赛者会看见三扇门,其中一扇门的里面有一辆汽车,选中里面是汽车的那扇门,就可以赢得该辆汽车,另外两扇门里面则都是一只山羊。当参赛者选定了一扇门,主持人会开启另一扇是山羊的门;并问:“要不要换一扇门?”

一、计算机仿真

import numpy as np
def choose(totlen):
    assert totlen>0 and isinstance(totlen,int)
    for i in range(totlen):
        if totlen*np.random.rand()<i+1:
            return i
class theproblem:
    def __init__(self,num_doors):
        assert num_doors>0 and isinstance(num_doors,int)
        self.num_doors=num_doors
        self.car_no=choose(self.num_doors)
        self.unopens=list()
        self.select=-1
        for i,v in enumerate(range(self.num_doors)):
            self.unopens.append(i)
    
    def get_goat(self,opendoor=True):
        if len(self.unopens)<=0:
            return None
        elif len(self.unopens)==1 and (self.unopens[0]==self.car_no or self.unopens[0]==self.select):
            return None
        elif len(self.unopens)==2 and (self.car_no in self.unopens and self.select in self.unopens and self.car_no!=self.select):
            return None
        goat=np.random.choice(self.unopens,1)[0]
        while goat==self.car_no or goat==self.select:
            goat=np.random.choice(self.unopens,1)[0]
        if opendoor:
            self.unopens.remove(goat)
        return goat
    
    def _open_door(self,door_no):
        self.unopens.remove(door_no)
        return (door_no==self.car_no,door_no)
    
    def select_door(self,switch=False,toopen=False):
        pre=self.select
        if switch and len(self.unopens)>1:
            while pre==self.select:
                self.select=np.random.choice(self.unopens,1)[0]
        elif self.select<0:
            self.select=np.random.choice(self.unopens,1)[0]
        if toopen:
            return self._open_door(self.select)
        return self.select
    
    def restart(self):
        self.car_no=choose(self.num_doors)
        self.unopens=list()
        self.select=-1
        for i,v in enumerate(range(self.num_doors)):
            self.unopens.append(i)
def one_turn(theguess,debug=False):
    theguess.restart()
    v=theguess.select_door()
    if debug:
        print("First the player selected door {}.".format(v))
    v=theguess.get_goat()
    if debug:
        print("And behind the door {} is a goat.".format(v))
    switched=(np.random.rand()<0.5)
    if debug:
        print("Does the player switch his selected door? {}.".format(switched))
    res, no =theguess.select_door(switch=switched,toopen=True)
    if debug:
        print("Does the player win the car in his chosen door {}? {}.".format(no,res))
        print("Car is behind the door {}.".format(theguess.car_no))
    return (int(res),int(switched))
guess=theproblem(3)
turns=50000
resarr=np.zeros(turns,dtype=int)
switcharr=np.zeros(turns,dtype=int)
for i in range(turns):
    resarr[i],switcharr[i]=one_turn(guess)
def pair_counts(arr1,arr2,val1,val2):
    match1=(arr1==val1)
    match2=(arr2==val2)
    return sum(match1*match2)
ff=pair_counts(resarr,switcharr,0,0)
tf=pair_counts(resarr,switcharr,0,1)
ft=pair_counts(resarr,switcharr,1,0)
tt=pair_counts(resarr,switcharr,1,1)
print("Percentage of not having a switch and lose: {}%".format(100*ff/turns))
print("Percentage of having a switch and lose: {}%".format(100*tf/turns))
print("Percentage of not having a switch and win: {}%".format(100*ft/turns))
print("Percentage of having a switch and win: {}%".format(100*tt/turns))
Percentage of not having a switch and lose: 33.156%
Percentage of having a switch and lose: 16.582%
Percentage of not having a switch and win: 16.692%
Percentage of having a switch and win: 33.57%
print("Percentage of having a win if switched: {0:2.3f}%".format(tt/(tf+tt)*100))
print("Percentage of having a switch in win: {0:2.3f}%".format(tt/(ft+tt)*100))
print("Percetage of the cases where switch is better: {0:2.3f}%".format((tt+ff)/turns*100))
print("Percetage of the cases where switch is worse: {0:2.3f}%".format((tf+ft)/turns*100))
Percentage of having a win if switched: 66.937%
Percentage of having a switch in win: 66.790%
Percetage of the cases where switch is better: 66.726%
Percetage of the cases where switch is worse: 33.274%

二、理论论证

S∈{0,1}S\in\{0,1\}S{0,1} 表示选手是否改变选择,W∈{0,1}W\in\{0,1\}W{0,1}表示该选手是否赢得汽车。那么各事件的概率就可以表示为P(S,W)P(S,W)P(S,W)。设选手改变选择的概率为ppp。如果主持人知道每扇门背后有什么东西:
P(0,0)=23−23p P(0,0)=\frac{2}{3}-\frac{2}{3}p P(0,0)=3232p

P(0,1)=13−13p P(0,1)=\frac{1}{3}-\frac{1}{3}p P(0,1)=3131p

P(1,0)=13p P(1,0)=\frac{1}{3}p P(1,0)=31p

P(1,1)=23p P(1,1)=\frac{2}{3}p P(1,1)=32p
p=12p=\frac{1}{2}p=21时,其结果和计算机仿真的结果相吻合。

然而,如果主持人不知道每扇门背后有什么,他不过是运气好到每次都能猜对,那么:
P(0,0)=13−13p P(0,0)=\frac{1}{3}-\frac{1}{3}p P(0,0)=3131p

P(0,1)=13−13p P(0,1)=\frac{1}{3}-\frac{1}{3}p P(0,1)=3131p

P(1,0)=13p P(1,0)=\frac{1}{3}p P(1,0)=31p

P(1,1)=13p P(1,1)=\frac{1}{3}p P(1,1)=31p
如此我们就能得到
P(W=1∣S=1)=13,P(W=1∣S=0)=13 P(W=1\mid S=1)=\frac{1}{3}, P(W=1\mid S=0)=\frac{1}{3} P(W=1S=1)=31,P(W=1S=0)=31
这正好符合我们的直觉,即机会是“对半开”的。然而这个直觉不符合数学证明的要求,因为概率论要求全部可能事件的概率之和为1。这也就意味着,当我们在用直觉思考三门问题时,我们实际是假定了主持人在独立地做出选择,然后排除那些选错的可能性,我们用这样的思维模式来逼近“主持人在知晓的情况下做选择”这样的情形。但概率论不是这样来计算的。概率论必然会将每一个可能事件映射到[0,1][0,1][0,1]区间,来作为该事件的概率。这种“排除可能性vs事件必然有概率”的差异,在我看来就是反直觉的源头。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值