三门问题(Monty Hall Problem)是概率统计中的一个著名的问题,原问题大概如此:
Monty Hall是美国电视节目的主持人,在这个节目中,有一个给嘉宾开奖的环节,开奖的环节是这样的:现在一共有三扇门,其中有一扇门背后是一辆汽车,另外两扇门背后是山羊。现在嘉宾可以选择一扇门,此时主持人会打开另外两扇门中一扇背后是山羊的门,然后会问嘉宾是否要更换之前的选择。嘉宾选择更换或不更换后,主持人会开启嘉宾最终选择的那一扇门,假如开启之后是一辆汽车的话,那么就会将汽车送给嘉宾。
在这个问题中,看似换与不换的概率都是不变的,都是1/3。但是实际上更换选择后能够得到汽车的概率是2/3,不更换选择能够得到汽车的概率是1/3。这个问题的解释方法有很多种,比较严谨的方法是利用贝叶斯公式,概率论中的解释此处不再加以赘述。
虽然从概率论的角度能够严谨地理解三门问题,但是仍然不够直观。如果想要直观地感受这个概率,就需要亲自做试验来感受试验中两种情况分别的频率。不过,鉴于每次和别人玩都需要提供一辆汽车,实在太过费钱,无法将试验进行下去。所以我决定找一个可以自己和自己玩的好朋友(没错,就是python),让他自己和自己玩足够多次数后,将频率告诉我们。
这是代码运行后的结果:
请输入要做三门问题的次数:1000000
若换,做1000000次实验,开启正确的门的频率是0.666369
若不换,做1000000次实验,开启正确的门的频率是0.332320
接下来开始上代码:
这个程序要依靠python中的random模块,其中主要是random.choice()这个函数。
下面是python自己和自己玩三门游戏的函数:
import random as r
def do_monty_hall (determine):
'''determine变量只能选择'change'或者'maintain' 选择‘change’表示要换门,选择'maintain'表示不换门. 返回值是此次游戏结束后,是否选对了背后是车子的门, 如果选对了,返回值就是1,选错了返回值就是0.'''
name_dict = { 'door1':0,'door2':0,'door3':0}
#创建包含三门名字和是否有车的字典,其中0表示门后面是山羊,1表示门后面有车子
#此时还未确定哪扇门后面是车子
name_list = ['door1','door2','door3']
#创建包含三门名字的列表
ans = r.choice(name_list)
#利用随机函数,随机选定一个门后面有车子
name_dict[ans] = 1
choice = r.choice(name_list)
#choice表示嘉宾选择的门
name_list.remove(choice)
open_door = r.choice(name_list)
#open_door表示主持人开启的那扇有山羊的门
if name_dict[open_door] == 0 :
name_list.remove(open_door)
change_door = name_list[0]
else :
change_door = open_door
#用随机选择加判断语句决定主持人开启哪一扇门来提示嘉宾
if determine == 'change' :
if name_dict[change_door] == 1 :
return 1
else :
return 0
elif determine == 'maintain' :
if name_dict[choice] == 1 :
return 1
else :
return 0
#此部分determine的选择和返回值可以参看前面的函数文档
该函数运行的结果的1和0分别代表了选对有车子的门和没有选对,这样就可以统计选对的次数。
主函数如下:
if __name__ == '__main__' :
n = int(input('请输入要做三门问题的次数:'))
right_time1 = 0
right_time2 = 0
k = 0
while k <= n:
right_time1 += do_monty_hall('change')
right_time2 += do_monty_hall('maintain')
k+=1
print('若换,做%d次实验,开启正确的门的频率是%f\n'%(n,(right_time1/n)))
print('若不换,做%d次实验,开启正确的门的频率是%f\n'%(n,(right_time2/n)))
这部分就是执行用户想要的做三门游戏的次数,然后计算出换与不换分别的频率,并输出。可以通过一个循环调用上面定义的函数实现。
完整的代码是这样的:
import random as r
def do_monty_hall (determine):
'''determine变量只能选择'change'或者'maintain' 选择‘change’表示要换门,选择'maintain'表示不换门. 返回值是此次游戏结束后,是否选对了背后是车子的门, 如果选对了,返回值就是1,选错了返回值就是0.'''
name_dict = { 'door1':0,'door2':0,'door3':0}
#创建包含三门名字和是否有车的字典,其中0表示门后面是山羊,1表示门后面有车子
#此时还未确定哪扇门后面是车子
name_list = ['door1','door2','door3']
#创建包含三门名字的列表
ans = r.choice(name_list)
#利用随机函数,随机选定一个门后面有车子
name_dict[ans] = 1
choice = r.choice(name_list)
#choice表示嘉宾选择的门
name_list.remove(choice)
open_door = r.choice(name_list)
#open_door表示主持人开启的那扇有山羊的门
if name_dict[open_door] == 0 :
name_list.remove(open_door)
change_door = name_list[0]
else :
change_door = open_door
#用随机选择加判断语句决定主持人开启哪一扇门来提示嘉宾
if determine == 'change' :
if name_dict[change_door] == 1 :
return 1
else :
return 0
elif determine == 'maintain' :
if name_dict[choice] == 1 :
return 1
else :
return 0
#此部分determine的选择和返回值可以参看前面的函数文档
if __name__ == '__main__' :
n = int(input('请输入要做三门问题的次数:'))
right_time1 = 0
right_time2 = 0
k = 0
while k <= n:
right_time1 += do_monty_hall('change')
right_time2 += do_monty_hall('maintain')
k+=1
print('若换,做%d次实验,开启正确的门的频率是%f\n'%(n,(right_time1/n)))
print('若不换,做%d次实验,开启正确的门的频率是%f\n'%(n,(right_time2/n)))
通过输入一个较大的游戏次数,我们就可以直观地体会到三门问题中换与不换的选择分别对应多少的频率。