Monty Hall(娱乐节目抽奖游戏)蒙特卡洛模拟:
1. 模拟背景描述:
1)有x个门,某个门后面有奖,剩余的门后是骆驼。
2)嘉宾选择一个门。
3)主持人打开剩下的x-1个门中的一个,但只能打开无奖的门。
4)此时,主持人问嘉宾要不要换门?还是坚持首次选择?
问题: 嘉宾应不应该换?
2. 蒙特卡洛算法原理:
基于大数定理与中心极限定理对随机事件进行模拟。
3. 程序分如下几部:
1) 设定奖(即奖分布随机的情况下,嘉宾赢不赢改换)
2)嘉宾选择门号
3)主持人选择门号(不能是奖门,以及嘉宾选择的门)
4)不换赢的概率,换赢的概率。
5)模拟x次
4. python实现
import os
import random
from __future__ import division
#生成连续的整型序列
def intSeq(num):
seq=[]
for i in range(num):
seq.append(i+1)
return seq
#随机生成有奖的门号
def getPrize(num):
prize=random.choice(intSeq(num))
return prize
#嘉宾选择
def getChoicebyGuest(num):
guest=random.choice(intSeq(num))
return guest
#主持人选择,不能选择有奖的门和嘉宾已选择的门
def getChoicebyHost(prize,guest,num):
choice_set=intSeq(num)
if prize in choice_set:
choice_set.remove(prize)
if guest in choice_set:
choice_set.remove(guest)
host=random.choice(choice_set)
return host
# 嘉宾不换与换,是否赢
def isWin(prize,guest,host,num):
choice_set=intSeq(num)
if guest in choice_set:
choice_set.remove(guest)
if host in choice_set:
choice_set.remove(host)
is_notchange_win=0
is_change_win=0
if prize==guest:
is_notchange_win=1
change_num=random.choice(choice_set) #如果换,除嘉宾和主持人门号外,剩下随机选择一个
if prize==change_num:
is_change_win=1
return is_notchange_win,is_change_win,change_num
#多次模拟
def testPro(times,num):
result=[]
for i in range(times):
prize=getPrize(num)# 选取有奖门号
guest=getChoicebyGuest(num) #嘉宾选择门号
host=getChoicebyHost(prize,guest,num) #主持人选择门号
is_notchange_win,is_change_win,change_num=isWin(prize,guest,host,num) #换与不换是否赢。
test_result=','.join([str(prize),str(guest),str(host),str(change_num),str(is_notchange_win),str(is_change_win)])
result.append(test_result)
return result
#设定场景,例如:嘉宾选1号门,主持人选3号门时,根据换与不换赢的概率决定是否要换。
def winPro(guest_choice,host_choice,result):
chage_flag=0 #要不要换门号(如果换后赢的概率大于未换的概率,则换门号)
times_item=0 #模拟多次中某一场景出现的次数,例如:模拟100万次,嘉宾选1号门,主持人选3号门,该场景出现的次数。
no_change_win=0 #不换,赢标志
change_win=0 #换,赢标志
for i in range(len(result)):
test_result=result[i].split(',')
if int(test_result[1])==guest_choice and int(test_result[2])==host_choice: #符合指定场景
times_item=times_item+1
if int(test_result[4])==1:
no_change_win=no_change_win+1 #该场景下,不换赢的次数
if int(test_result[5])==1:
change_win=change_win+1 #该场景下,换赢的次数。
no_change_pro=no_change_win/times_item #不换赢概率
change_pro=change_win/times_item #换赢概率
if change_pro>no_change_pro: #如果换赢的概率大于不换赢的概率,则换。
chage_flag=1
return no_change_pro,change_pro,chage_flag,times_item
if __name__=='__main__':
door_num=4 #门的个数
times=1000000 #模拟次数
result=testPro(times,door_num)
for i in range(1,door_num+1): #遍历所有可能出现的场景
for j in range(1,door_num+1):
if i<>j: #嘉宾和主持人选择不能一样。
no_change_pro,change_pro,chage_flag,times_item=winPro(i,j,result)
print ('test_times:%d'%times_item,'guest: %d'%i,'host:%d'%j,'%d'%chage_flag,'%.2f'% change_pro,'%.2f'% no_change_pro)
5、效果
1)以逗号间隔,各字段含义 test_times:该场景出现次数,guest: 该场景嘉宾选择门号,host:该场景主持人选择门号,换,换赢的概率,不换赢的概率
例如: 总共4道门,模拟100万次,嘉宾选择1号门,主持人选择2号门时,嘉宾【换】赢的概率大(0.37>0.25)。
('test_times:82806', 'guest: 1', 'host:2', '1', '0.37', '0.25')
('test_times:83364', 'guest: 1', 'host:3', '1', '0.37', '0.25')
('test_times:83230', 'guest: 1', 'host:4', '1', '0.37', '0.25')
('test_times:83372', 'guest: 2', 'host:1', '1', '0.37', '0.25')
('test_times:83506', 'guest: 2', 'host:3', '1', '0.38', '0.25')
('test_times:83529', 'guest: 2', 'host:4', '1', '0.37', '0.25')
('test_times:83427', 'guest: 3', 'host:1', '1', '0.37', '0.25')
('test_times:83247', 'guest: 3', 'host:2', '1', '0.37', '0.25')
('test_times:83327', 'guest: 3', 'host:4', '1', '0.38', '0.25')
('test_times:83593', 'guest: 4', 'host:1', '1', '0.37', '0.25')
('test_times:83405', 'guest: 4', 'host:2', '1', '0.38', '0.25')
('test_times:83194', 'guest: 4', 'host:3', '1', '0.38', '0.25')