1. 苏葳的备忘录首页
  2. 编程

蒙提霍尔问题(三门问题)的Python模拟过程

python wxpython boa

假设你正在参加一个游戏节目,你被要求在三扇门中选择一扇。其中一扇后面有一辆车,其余两扇后面则是山羊。你选择了一道门,假设是一号门,然后,事先知道门后面有什么的主持人,开启了另一扇后面有山羊的门,假设是三号门。然后他问你:“你想选择二号门吗?”,此时,改变你的选择对你会是更好的选择吗?

这就是著名的“蒙提霍尔问题”,也被称为“三囚犯问题”或者“贝特朗箱子悖论”。后来的研究者们对问题做出了更确切的描述:

参赛者在三扇门中挑选一扇。他并不知道内里有什么。
主持人知道每扇门后面有什么。
主持人必须开启剩下的其中一扇门,并且必须提供换门的机会。
主持人永远都会挑一扇有山羊的门。
如果参赛者挑了一扇有山羊的门,主持人必须挑另一扇有山羊的门。
如果参赛者挑了一扇有汽车的门,主持人随机在另外两扇门中挑一扇有山羊的门。
参赛者会被问是否保持他的原来选择,还是转而选择剩下的那一道门。

虽然答案已有定论,下面还是用Python来模拟这个选择的过程。此程序假设一个门后有羊,两个门后是空的,随机生成各种排列,重复主持人和参赛者的各种选择多次,来验证参赛者选择换门后得到羊的机率:

#-*-coding:gbk -*-
import random
class ThreeDoor(object):
	doors=[]   #初始化门结构[[0,0],[0,0],[0,0]],第一维用来放羊,第二维用来记录选手和主持人的选择。每个门后是否有羊(1有,0无),是否选择此门(未选择0,选手选1,主持人选2)
	doorcount=3
	def __init__(self):  #初始化三个门[[0,0],[0,0],[0,0]],然后随机往一个门后放只羊(置为1),如[[0,0],[1,0],[0,0]],表示随机往第二门后放了只羊
		self.doors=[]
		which=random.randint(1,self.doorcount)
		for a in range(1,self.doorcount+1):
			if (a==which):
				self.doors.append([1,0])
			else:
				self.doors.append([0,0])
		print('init:\n',[ a[0] for a in self.doors],'\n',[ a[1] for a in self.doors])
	def __del__(self):
		self.doors=[]
	def choice1(self):  #选手在3个门中随机选择1个门(置为1):[[0,0],[1,0],[0,1]]表示选手选择了第3个门
		ch1=random.randint(1,self.doorcount)
		self.doors[ch1-1][1]=1
		print('choice1:\n',[ a[0] for a in self.doors],'\n',[ a[1] for a in self.doors])
	def choice2(self):  #主持人选择1个没有羊的门(置为2)(若有羊的门已被选手选到,则在剩下2门中随机选1个门)如:[[0,2],[1,0],[0,1]]
		set2=list(map(lambda x,y:x+y,[a[0] for a in self.doors],[a[1] for a in self.doors]))
		print('set2:',set2)
		if max(set2)==2:
			w=random.randint(1,self.doorcount-1)
			b=[ i for (i,item) in enumerate(set2,start=0) if item!=2]
			self.doors[b[w-1]][1]=2
		elif max(set2)==1:
			self.doors[set2.index(0)][1]=2			
		print('choice2:\n',[ a[0] for a in self.doors],'\n',[ a[1] for a in self.doors])
		
	def summ(self):    #此时还有1个门选手和主持人都没有选。计算此门后有羊的机率(也就是选手改选此门后得到羊的机率)
		return self.doors[[a[1] for a in self.doors].index(0)][0]   #只需返回未选的门(列表第二维值为0的索引,因为选手选的为1,而主持人选的为2)的第一维值是否为1(1表示有羊)
	
if __name__=='__main__':
	success=0    #换门后命中羊的次数
	testcount=100000 #总测试次数
	print('总测试次数:',testcount)
	for a in range(0,testcount):
		print('第%d次测试:'%a)
		n=ThreeDoor()
		n.choice1()
		n.choice2()
		success=success+n.summ()
		print('当前换门得羊成功率%f'%(success/(a+1)))
		del n
	

程序设置了模拟10万次,其实运行1千次跟10万次结果不会有太大的出入:

第99998次测试:
init:
 [0, 1, 0]
 [0, 0, 0]
choice1:
 [0, 1, 0]
 [0, 1, 0]
choice2:
 [0, 1, 0]
 [2, 1, 0]
当前换门得羊成功率0.665697
第99999次测试:
init:
 [0, 0, 1]
 [0, 0, 0]
choice1:
 [0, 0, 1]
 [0, 0, 1]
choice2:
 [0, 0, 1]
 [0, 2, 1]
当前换门得羊成功率0.665690

可看到,验证的结果跟此问题的答案一致,参赛者在主持人询问时选择换门,确实能把命中羊的机率提高到2/3。

原创文章,作者:苏葳,如需转载,请注明出处:https://www.swmemo.com/2220.html

发表评论

邮箱地址不会被公开。 必填项已用*标注