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

关于“三门问题(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)。设选手改变选择的概率为 p p p。如果主持人知道每扇门背后有什么东西:
P ( 0 , 0 ) = 2 3 − 2 3 p P(0,0)=\frac{2}{3}-\frac{2}{3}p P(0,0)=3232p

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

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

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

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

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

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

P ( 1 , 1 ) = 1 3 p P(1,1)=\frac{1}{3}p P(1,1)=31p
如此我们就能得到
P ( W = 1 ∣ S = 1 ) = 1 3 , P ( W = 1 ∣ S = 0 ) = 1 3 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事件必然有概率”的差异,在我看来就是反直觉的源头。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Sure, I can help you with that. Here is the code to simulate the Monty Hall problem: ```python import numpy as np import matplotlib.pyplot as plt def random_door(): return np.random.choice([1,2,3]) def monty_choice(contestant_door, car_door): if contestant_door == car_door: return np.random.binomial(1, 0.5) + 1 else: return 6 - contestant_door - car_door def win_car(strategy, contestant_door, car_door, monty_door): if strategy == 'switch': new_door = 6 - contestant_door - monty_door return new_door == car_door else: return contestant_door == car_door def simulation(n, strategy): car_door = random_door() contestant_door = random_door() monty_door = monty_choice(contestant_door, car_door) successes = 0 failures = 0 for i in range(n): if win_car(strategy, contestant_door, car_door, monty_door): successes += 1 else: failures += 1 return successes, failures n = 1000 switch_successes, switch_failures = simulation(n, 'switch') noswitch_successes, noswitch_failures = simulation(n, 'noswitch') fig, axs = plt.subplots(1, 2, figsize=(10, 5), sharey=True) axs[0].bar(['Switch Successes', 'Switch Failures'], [switch_successes, switch_failures]) axs[0].set_title('Switch Strategy') axs[1].bar(['No Switch Successes', 'No Switch Failures'], [noswitch_successes, noswitch_failures]) axs[1].set_title('No Switch Strategy') plt.show() ``` This code defines three functions: `random_door` to randomly select a door for the car or the contestant, `monty_choice` to choose the door Monty opens based on the contestant's choice and the location of the car, and `win_car` to determine if the contestant wins the car based on the strategy (switch or no switch), the doors chosen by the contestant and Monty, and the location of the car. The `simulation` function runs the simulation for a given strategy and number of iterations. It selects the doors for the car and the contestant, chooses the door Monty opens, and then checks if the contestant wins the car based on the chosen strategy. The code then runs the simulation for both the switch and no switch strategies, and plots the results in side-by-side bar charts. According to the simulation results, the contestant should switch doors to increase their chances of winning the car. The switch strategy has a higher probability of success than the no switch strategy. This result is consistent with the conditional probability of the problem, which shows that the probability of winning the car is 2/3 if the contestant switches doors, and 1/3 if they do not switch.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值