用Python解决蒙提霍尔问题
我正在尝试理解蒙提霍尔问题的这个解决方案,我大部分代码都明白了,但有两个地方我卡住了。
下面是代码,但我特别卡在这两个部分
result[bad] = np.random.randint(0,3, bad.sum())
还有整个 switch_guess
函数。
如果有人能用简单易懂的语言给我解释一下,那就太好了。
#Simulates picking a prize door
def simulate_prizedoor(nsim):
return np.random.randint(0,3,(nsim))
#Simulates the doors guessed
def simulate_guesses(nsim):
return np.zeros(nsim, dtype=np.int)
#Simulates the "game host" showing whats behind a door
def goat_door(prize_doors, guesses):
result = np.random.randint(0,3, prize_doors.size)
while True:
bad = (result == prize_doors) | (result == guesses)
if not bad.any():
return result
result[bad] = np.random.randint(0,3, bad.sum())
#Used to change your guess
def switch_guess(guesses, goat_doors):
result = np.zeros(guesses.size)
switch = {(0, 1): 2, (0, 2): 1, (1, 0): 2, (1, 2): 1, (2, 0): 1, (2, 1): 0}
for i in [0,1,2]:
#print "i = ", i
for j in [0,1,2]:
#print "j = ", j
mask = (guesses == i) & (goat_doors == j)
#print "mask = ", mask
if not mask.any():
continue
result = np.where(mask, np.ones_like(result) * switch[(i, j)], result)
return result
#Calculates the win percentage
def win_percentage(guesses, prizedoors):
return 100 * (guesses == prizedoors).mean()
#The code to pull everything together
nsim = 10000
#keep guesses
print "Win percentage when keeping original door"
print win_percentage(simulate_prizedoor(nsim), simulate_guesses(nsim))
#switch
pd = simulate_prizedoor(nsim)
guess = simulate_guesses(nsim)
goats = goat_door(pd, guess)
guess = switch_guess(guess, goats)
print "Win percentage when switching doors"
print win_percentage(pd, guess)
2 个回答
这也让我困惑,直到五分钟前我终于搞明白了。既然第一个问题已经解决了,我就只谈第二个。
简单来说:给定一系列的(猜测,山羊门),在(i,j)循环中,总会有一些模拟(比如说,simulation[0]和simulation[5])被(i,j)“命中”,也就是说,第0个和第5个模拟的猜测是i,山羊门是j。
变量mask
在这个例子中记录了0和5。然后第0个和第5个的结果就可以确定,因为在这些模拟中,唯一可以切换的门是由i和j决定的。所以np.where
会更新这些模拟的结果,而其他模拟则保持不变。
以上就是我的直觉。如果你想理解我在说什么,你需要知道np.where
是怎么工作的。祝你好运。
… 具体来说,我在这两个部分卡住了
result[bad] = np.random.randint(0,3, bad.sum())
我们来把这个问题拆分开。可以把那个 10000
改成一个小一点的数字,比如 5
,这样你就可以打印出这些值(可以用 print
函数,或者在调试器里查看),看看发生了什么。
当我们开始这个函数时,prize_doors
会有 5 个从 0
到 2
的随机值,比如 2 2 0 1 2
,而 guesses
会有 5 个值,全部是 0,像 0 0 0 0 0
。所以,result
一开始会有 5 个随机值,从 0 到 2,比如 0 2 2 0 1
。
每次第一次进入循环时,bad
会是一个包含 5 个 bool
值的列表,如果 result
中的值和 prize_doors
或 guesses
中的对应值匹配,那么这个值就是 True
。所以在这个例子中,bad
可能是 True True False True False
,因为第一个猜测和 prize_doors
匹配,而第 0 和第 3 个猜测和 goats
匹配。
不幸的是,我们会一直在这个循环里转,因为循环里面没有任何东西会改变 result
,所以 bad
会永远保持不变,而一直做同样的检查总是会返回相同的值。
但是,如果你把那行 result[bad] = …
缩进到循环里面,那就完全不同了。所以,我们假设这就是你应该做的,只是复制错了。
当 True
和 False
被当作数字时,它们的值分别是 1
和 0
。所以,bad.sum()
就是计算 bad
中有多少个匹配的值——在这个例子中是 3
。
因此,np.random.randint(0, 3, bad.sum())
会从 0
到 2
中随机选择 3 个值,比如 1 0 1
。
现在,result[bad]
会选择 result
中所有对应 bad
为 True
的元素,所以在这个例子中是 result[0]
、result[1]
和 result[3]
。
所以我们把 1 0 1
赋值给这三个选中的位置,结果 result
现在变成了 1 0 2 1 1
。
接下来再进入循环时,bad
现在是 True False False False False
。我们还有至少一个 True
值,所以再次执行 result[bad] =
这一行。这次,bad.sum()
是 1
,所以我们随机选择 1 个值,比如 0
,然后把这个值赋给 result[0]
,所以 result
现在变成了 0 0 2 1 1
。
下次进入时,bad
现在是 False False False False False
,所以 bad.any()
是 False
,这就结束了。
换句话说,每次循环中,我们都会把那些不匹配奖品门或山羊门的值选出来,并为它们选择一个新门,直到最后没有这样的值为止。