The other night a maths teacher and I were sharing a few cold drinks and we fell to discussing the Monty Hall Problem.
Now, you may have heard of the Monty Hall Problem even if you don’t know it by that name. It goes like this: you are on a TV game show. There are 3 doors. Behind one door is a car, behind the other two there are goats. Assuming you want to win the car (thank you, Son B), you pick a door; the host then opens a door to reveal a goat and gives you the chance to change your choice. Should you change your choice, stick with the one you have, or does it make no difference?
This problem has infuriated me for ages because I’ve always been firmly of the opinion that it makes no difference. Which is a problem, because, in fact, if you change your choice you are TWICE as likely to win the car as if you stick with your first choice. To most people, this seems utterly mad, certainly counter-intuitive.
My maths teacher friend tried explaining it to me with glove puppets and I still didn’t really get it, so I decided the best thing was for me to write a computer simulation of it in Python. And by golly, not only does it prove him right, it also helped me understand the problem in a way I never had previously.
I think this would make a great combined maths/computing activity, because even just planning out the code helped me see the logic of the problem in a way I never had before because I was forced to confront what happens in every possible scenario. I could then see that being told what one of the wrong doors was actually changes everything – or at least it gives you a whole lot more information and that’s the key to seeing why you should change your choice.
To simplify the coding, I decided that for each game I’d always have the car behind the third door (which is door number 2, of course, Python being a zero-indexed language). I don’t think this makes any difference as long as the door choices the players make are random – an extension activity would be to rewrite the code so in every game the car is behind a different door.
This is how my code works – I’d be very happy to hear more elegant solutions, but it was when I wrote this out that the penny dropped and I finally understood the problem. Here’s how my (possibly flawed) thinking worked:
|-----------------------|
door no.| 0 | 1 | 2 |
|-----------------------|
| goat | goat | car |
|-----------------------|
I decided that I’d have two players who both pick the SAME random door in each game; player 1 would stick and player 2 would always change their choice having been ‘shown’ the door with the goat. If the car is always behind the last door, I think the sequence works like this…
If the players choose door 0, the host must open door 1 to reveal a goat, and player 2 must change their choice to door 2 – winning the car!
If the players choose door 1, the host must open door 0 to reveal a goat, and player 2 must change their choice to… door 2 – winning a car again!
If the players choose door 2 (containing the car), it doesn’t matter which of the other 2 doors the host opens, but I have decided they always open door 0 to keep things simple. In this case player 1 actually wins the car, but it’s the only way they can win it – a 1 in 3 chance. With bitter irony, player 2 has actually picked the right door first time but they will be forced to change their choice and win a goat instead – but the crucial thing is that they won in 2 out of the 3 scenarios. The changing player has a 2 in 3 chance of winning, the stick-in-the-mud has only a 1 in 3 chance.
Just sketching this out in a simple flowchart aids understanding of this problem, I don’t think any actual programming is necessary. You can see how the player who changes is twice as likely to win a car:
And indeed when I run this code for about 1000 games, player 2 does indeed win about twice as many cars as player 1:
A nice side-bar to this (especially as we teach in a girls’ school) is that it was a woman, Marilyn vos Savant, who proposed the correct solution to this problem, and 1000s of people with PhDs (mostly men, I bet) wrote to say she was wrong. She wasn’t.
Here’s my code, run it yourself in Python3. You can also download it here. It runs an awful lot faster in the OS X command line than it does in IDLE, and I expect I’ll find the same with Windows.
If you improve it or use it in school, please let me know! Some bar charts might be nice…
# A computer simulation of the Monty Hall problem,
# a counter-intuitive logic and probability puzzle.
# by @blogmywiki - www.suppertime.co.uk/blogmywiki
print('Welcome to the\n'
' __ ___ __ __ __ ____\n'
' / |/ /___ ____ / /___ __ / / / /___ _/ / /\n'
' / /|_/ / __ \/ __ \/ __/ / / / / /_/ / __ `/ / / \n'
' / / / / /_/ / / / / /_/ /_/ / / __ / /_/ / / / \n'
'/_/ /_/\____/_/ /_/\__/\__, / /_/ /_/\__,_/_/_/ \n'
' /____/ show\n'
' |-----------------------|\n'
'door no.| 0 | 1 | 2 |\n'
' |-----------------------|\n'
' | goat | goat | car |\n'
' |-----------------------|\n')
while True:
import random
print()
answer = input('How many games do you want to simulate? (q to quit) ')
if answer == "q":
break
else:
tries = int(answer)
doors = ['goat','goat','car']
# door no. 0 1 2
# I don't think it matters that these are not random
# and keeping this fixed makes coding easier.
# Both players will pick the same door, but
# when a door is opened with a goat behind it,
# player1 will always stick with their 1st choice
# whereas player2 will always change.
# We will then keep a tally of who won more cars.
player1_tally = 0
player2_tally = 0
for i in range(tries):
chosen_door = random.randint(0,2)
# pick a random door number between 0 and 2
print('Game',i)
print ('door',chosen_door,'chosen, containing a',doors[chosen_door])
player1_choice = chosen_door
if chosen_door == 0:
player2_choice = 2
print('Host opens door 1, player2 switches choice to door 2')
elif chosen_door == 1:
player2_choice = 2
print('Host opens door 0, player2 switches choice to door 2')
elif chosen_door == 2:
player2_choice = 1
print('Host opens door 0, player2 switches choice to door 1')
if doors[player1_choice] == "car":
print('Player 1 won a car!')
player1_tally = player1_tally + 1
if doors[player2_choice] == "car":
print('Player 2 won a car!')
player2_tally = player2_tally + 1
print()
print('Player 1 who stuck won',player1_tally,'cars.')
print('Player 2 who changed won',player2_tally,'cars.')
And here’s a Trinket with a Python 2(ish) version of the code which you should be able to run in a web browser: