码迷,mamicode.com
首页 > 其他好文 > 详细

反直觉的一个游戏 - 三门问题 (Monty Hall problem)

时间:2015-06-25 16:59:45      阅读:422      评论:0      收藏:0      [点我收藏+]

标签:

三门问题,也叫蒙提霍尔问题(Monty Hall Problem)

以电视节目 - Let‘s make a deal的主持人蒙提霍命名的一个反直觉问题。

游戏简介

假设有3个门。 其中一个后面藏着宝藏,其余2个都是空门(美国节目中1个后面有汽车,其余的2个是山羊)。 

技术分享

游戏开始:

首先你先选择一张门,

选好后,主持人帮你在其余2扇没有被选择的门中打开一扇没奖的门。

技术分享

 

这时候你有一个选择权, 换门还是不换门?

直觉上换不换几率都是50%,那到底几率是多少? 换是不是得奖的机会大一些?

可以用代码来模拟一遍。python

编程就是一步一步来,当然有时候要想清楚直接到达想要到的地方。

第一遍我们就用一步步来的方法走一遍吧。 (代码一部分借鉴了哈佛的CS109)

首先我们先定义下 有3扇门,其中奖品在某一扇门中。 

 

首先安装下 numpy库

1.这个可以用 random.randint来实现。 randint(start, end, size) 随机选择的3个门中间的一个

1 def simulate_prizedoor(nsim):
2     return np.random.randint(0, 3, (nsim))

2.然后我们可以定义下,一开始选择的一扇门。 这里可以用固定选择法,也可以用随机法,数据大的情况下差别不大。这里我们直接用了固定选择第一扇门(0)

1 def simulate_guess(nsim):
2     return np.zeros(nsim, dtype=np.int)

3.然后我们 模拟主持人,开一扇没有奖品的门。

1 def goat_door(prizedoors, guesses):
2     result = np.random.randint(0, 3, prizedoors.size)
3     while True:
4         bad = (result == prizedoors) | (result == guesses)
5         if not bad.any():
6             return result
7         result[bad] = np.random.randint(0, 3, bad.sum())

这里要实现的逻辑是, 先生成一个0到2的随机数。 然后匹配直到 不等于 我们一开始的选择的那扇门 或 宝藏存在的那扇门。 

 

4.然后模拟,我们假如我们在主持人打开门之后, 选择换一张门。

1 def switch_guess(guesses, goatdoors):
2     result = np.random.randint(0, 3, guesses.size)
3     while True:
4         bad = (result == guesses) | (result == goatdoors)
5         if not bad.any():
6             return result
7         result[bad] = np.random.randint(0, 3, bad.sum())

看得出来实现的代码和上面是一样的。 我们换的这扇门,不能是原来那扇,而且也不能是开了的那扇。

5.计算胜率。

1 def win_percentage(guesses, prizedoors):
2     return 100 * (guesses == prizedoors).mean()

这里需要注意, 如果单纯的bool type的数据是没有mean这个函数的。 np 把真假转换成了1,0这样才可以计算平均数。mean = 总值/数组的总数

 

6.最后我们就可以测试下,换和不换的区别了。 因为大数定律,我们测试100000遍

 1 pd = simulate_prizedoor(nsim)
 2 guess = simulate_guess(nsim)
 3 goats = goat_door(pd, guess)
 4 guess = switch_guess(guess, goats)
 5 
 6 nsim = 100000
 7 
 8 print "Win percentage when keeping original door"
 9 print win_percentage(simulate_prizedoor(nsim), simulate_guess(nsim))
10 
11 print "Win percentage when switching doors"
12 print win_percentage(pd, guess).mean()
Win percentage when keeping original door
33.32
Win percentage when switching doors
66.69

测试结果, 胜率方面 换能提高1倍的胜率。 (2个数相加超过100是因为小数点后的换算原因。)

测试完如果有点不能接受,我们换种方式

 

————————————————————————————一百遍的分割线——————————————————————————————————————

 

一切都不变的情况下,假如说有100扇门,你先选一扇,主持人帮你开98扇, 这时候也只剩下了2扇门。 你是换呢,还是不换。

技术分享

这个代码实现就有点小区别了。 因为大家熟悉了之前的实现方法, 我就换了一种实现方法。

1. 定义100扇门中有1个宝箱,这个做太多改变, 再定义我们随机选择了一扇门(之前是固定)

1 start = 0
2 end = 100
3 def simulate_prizedoor(nsim):
4     return np.random.randint(start, end, (nsim))
5
6 def simulate_guess(nsim):
7     return np.random.randint(start, end, (nsim))

2. 这里我们的主持人要关98扇门,真正要实现这个这个模拟,需要各种循环和条件。 其实根本没必要那么麻烦, 实现一个程序或者过程只要目的达到了就OK,不管是不是一模一样的模拟了一遍。  编程就是要跳出盒子,什么事都在盒子里面,永远无法做到效率的解决问题。

实现逻辑: 这里面不管主持人开多少门, 最后只会留2扇门。 这2扇门只有2种可能性:以猜中没猜中为逻辑

1. 我们猜中了, 所以我们猜的门 和 另外一扇随机留下的门

2. 我们猜错了,所以留下一扇我们猜的门,和一扇宝藏门。

实现这个代码很简单。

1 def guess_switch(guess_keep, pd): 
2     alter = np.random.choice([x for x in range(start,end) if ((x != guess_keep)&(x != pd)).any()])
3     num = np.where(guess_keep == pd,alter,pd)
4     return num

 

3. 胜率计算

 1 def win_percentage(guesses, prizedoors):
 2     return 100*(guesses == prizedoors).mean()
 3 
 4 nsim = 10000
 5 
 6 pd = simulate_prizedoor(nsim)
 7 guess_keep = simulate_guess(nsim)
 8 guess_sh = guess_switch(guess_keep, pd)
 9 
10 #keep guesses
11 print "Win percentage when keeping original door"
12 print win_percentage(pd, guess_keep)
13 
14 #switch
15 print "Win percentage when switching doors"
16 print win_percentage(pd, guess_sh)
Win percentage when keeping original door
1.04
Win percentage when switching doors
98.96

数据胜于雄辩。

PS:本人代码水平有限, 请大家多多指正。 另外一个问题, 有没有可能实现所有的if都用np.where来替代。 if和for真是效率太低了。

 

反直觉的一个游戏 - 三门问题 (Monty Hall problem)

标签:

原文地址:http://www.cnblogs.com/HGYD/p/4600235.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!