Suppose you’re on a game show, and you’re given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what’s behind the doors, opens another door, say No. 3, which has a goat. He then says to you, “Do you want to pick door No. 2?” Is it to your advantage to switch your choice?
The common answer is that you have 2/3 chance of finding the car if you switch.
The thing that’s always bugged me about this common phrasing (and the accompanying solution) is the assumption that the host always opens a door, revealing a goat. This isn’t exactly evident from the problem definition; we only know that the host knowingly revealed a goat. The host could have been doing so maliciously; revealing a goat only when the competitor chooses a car the first time around in hopes to lure her away from the prize. And I’m not just reading too much into the puzzle; we need to phrase this problem as a repeatable scenario in order to have any notion of probabilistic strategy. The hosts behavioral pattern (random, predetermined or whatever) is a key component to this repeatable scenario.
Still, the switching strategy does yield a higher probability of scoring a sweet ride, when we know that the host will always reveal a goat. Just to put a stake through the heart of my doubt about Monty Pyth Hall’s true benevolence, I wrote something in Python to settle this once and for all. The results are not deterministic because I don’t seed the random number generator with a constant before execution, but the manual runs all yielded values within reasonable ranges of one another. The empirical results are as the common probabilistic solution suggests:
Iterations
Stay wins
Switch wins
Count
1,000,000
332,765
667,235
Percent
100
33.28
66.72
Here’s the source, try it out if you don’t believe me:
#!/usr/bin/python
# Mustafa Paksoy, April 08
# Usage: ./monty.py [iterations] [debug (optional)]
# Empirically evaluate the outcome of the Monty Hall problem.
from random import choice
from sys import argv
def safeRem(list,num):
if num in list:
list.remove(num)
correct_stay=0
correct_switch=0
tries=int(argv[1])
debug=len(argv)>2
for i in range(tries):
car=choice(range(3))
pick=choice(range(3))
others=range(3)
safeRem(others, car)
safeRem(others, pick)
monty=choice(others)
others=range(3)
others.remove(monty)
others.remove(pick)
newpick=others[0]
if debug: print 'car', car, 'pick', pick, 'monty', monty, 'newpick', newpick,
if newpick == car:
correct_switch+=1
if debug: print 'switch'
elif pick == car:
correct_stay+=1
if debug: print 'stay'
else:
raise ValueError("you've got a bug")
print 'tries:', tries
print 'stay correct:', correct_stay, float(correct_stay)/tries
print 'switch correct:', correct_switch, float(correct_switch)/tries
I was playing around with these problems and I came across one about building truth tables for arbitrary logic statements. So I decided to write the tool that I wish I had when I was taking symbolic logic. The code isn’t particularly clean, but I especially like how the fixed_table implementation came out with generators. (Thanks to this blog post for pointing me towards 99 problems.)
#!/usr/bin/python
# Mustafa Paksoy. April 08.
# P48 from
# https://prof.ti.bfh.ch/hew1/informatik3/prolog/p-99/
from pprint import pprint
And=lambda x,y:x and y
Or=lambda x,y:x or y
Not=lambda x:not x
Impl=lambda x,y:Or(Not(x), y)
def fixed_table(numvars):
"""
Generate true/false permutations for the given number of variables.
So if numvars=2
Returns (not necessarily in this order):
True, True
True, False
False, False
False, True
"""
if numvars is 1:
yield [True]
yield [False]
else:
for i in fixed_table(numvars-1):
yield i + [True]
yield i + [False]
def truth_table(vars, expr):
"""
Takes an array of variables, vars, and displays a truth table
for each possible value combination of vars.
"""
for cond in fixed_table(len(vars)):
values=dict(zip(vars,cond))
yield cond + [eval_expr(values, expr)]
def eval_expr(values, expr):
"""
Takes a dictionary values {var1 : val1, var2 : val2} and a tuple
expr (lambda, var1, var2) returns evaluated value.
expr needs to be in a LISP like format (operator, arg1, arg2).
Returns the value of expr when variables are set according to
values.
"""
argarr=[]
for arg in expr[1:]:
if (type(arg) in [tuple, list]):
argarr.append(eval_expr(values, arg))
elif (arg in values):
argarr.append(values[arg])
else:
raise ValueError('Invalid expr')
return expr[0](*argarr)
if __name__ == '__main__':
# Print truth table for the Impl operator.
pprint([i for i in truth_table(['x','y'], (Impl, 'x', 'y'))])