A while ago I stumbled across a problem which was referred to as the Monty Hall Problem. A problem which at first sight seems really simple.

It refers to a puzzle loosely based on the American television game show called 'Lets Make a Deal' hosted originally by Monty Hall.

The host Monty Hall shows you three doors, and asks you to pick one.

Behind one of these doors is the car of your dreams.

Behind the other two is a goat.

Now Monty hall asks you to pick a door, which you do.

You will win whatever is behind that door.

Happy with your choice?

Now comes the difficult decision. Monty Hall asks would you like to change the door you have chosen?

To help you, Monty Hall opens one of the other doors to reveal a goat. He never reveals the car.

Do you swap doors?

Surely you have now gone from a 1 in 3 chance of winning a Car, to a 1 in 2?

Or have you?

Does it matter if you swap?

What do you all think?

This problem became famous when a reader wrote in to Parade magazines ‘Ask Marilyn’ section in 1990. Marilyn Vos Savant answered the question correctly. However 10,000 people replied to her answer, the majority claiming she was wrong.

Her claim is you should always swap, as you improve your chances of winning a car from 1/3 to 2/3.

My statistics are not good enough to argue conclusively if Marilyn was right or not. However I decided to simulate the game in Python to prove once and for all if you should always swap doors!

Let us first have a think about what our program needs to do.

- We need to set up a game with three doors. Each door needs either a goat or a car behind it. There should only be one car.
- We then need to simulate Monty Hall revealing one of the other doors. To make our program simpler we will assume we are always opening door one. If the position of the goats and car are random then it doesn't matter which door we initially chose.
- We need to be able to swap from our original choice when Monty Hall offers the swap.
- We need to simulate many games where we do not swap our choice and check what the outcome is.
- Then we need to do the same but we swap when offered by Monty Hall, and then check what the outcome is.
- Finally we print the outcomes.

Here is my code to solve the problem. Have a read through it and see what you understand. Then I will break this down and explain it line by line.

#Monty Hall Problem
from __future__ import print_function #Allows python3 use of print()
import random
#Variables used through out the program
noSwap = 0
swap = 0
winCountNoSwap = 0
loseCountNoSwap = 0
winCountSwap = 0
loseCountSwap = 0
numberOfTests = 1000000 #Alter this to change the number of simulations
##Function to set up the game.
##Returns list with three items. 1 Car and 2 Goats randomly positioned.
def setUpGame():
number = random.randint(0,2) #random number 0,1 or 2
blankDoors = [0,0,0] #sets up a new game
count = 0
for item in blankDoors:
if count == number:
blankDoors[count] = 'Car'
else:
blankDoors[count] = 'Goat'
count += 1
return blankDoors
##Function to simulate Monty Hall revealing a goat.
##Assumes contestant has door one.
##Reveals only a Goat in either door 2 or 3.
def reveal(currentGame):
randomReveal = random.randint(1,2)
if currentGame[randomReveal] == 'Car':
if randomReveal == 1:
randomReveal = 2
else:
randomReveal = 1
currentGame[randomReveal] = 'Reveal'
else:
currentGame[randomReveal] = 'Reveal'
return currentGame
##Simulates the player swapping doors.
##Ensures they do not pick the 'Reveal' door.
def swapOption(currentGame):
temp = currentGame[0]
if currentGame[1] == 'Reveal':
currentGame[0] = currentGame[2]
currentGame[2] = temp
else:
currentGame[0] = currentGame[1]
currentGame[1] = temp
return currentGame
##Runs through the game without swapping the door chosen.
while noSwap < numberOfTests:
currentGame = setUpGame()
#print (currentGame)
currentGame = reveal(currentGame)
#print (currentGame)
if currentGame[0] == 'Car':
winCountNoSwap += 1
else:
loseCountNoSwap += 1
noSwap +=1
##Runs through the game swapping the door chosen.
while swap < numberOfTests:
currentGame = setUpGame()
#print (currentGame)
currentGame = reveal(currentGame)
#print (currentGame)
currentGame = swapOption(currentGame)
#print (currentGame)
if currentGame[0] == 'Car':
winCountSwap += 1
else:
loseCountSwap += 1
swap +=1
##Calculates percentage of winning for each of the options and prints the result.
print ("Without swapping you win" ,float(winCountNoSwap) / (float(winCountNoSwap) + float(loseCountNoSwap))*100, "% of the time.")
print ("With swapping you win" ,float(winCountSwap) / (float(winCountSwap) + float(loseCountSwap))*100, "% of the time.")

The first line is simply a comment to state what the program is doing. Anything preceded by a # is ignored by the program.

#Monty Hall Problem

Next is a line I have started to use recently, as I think more about moving over to Python 3. This means I can use the same print functions in my program as used in Python 3.

from __future__ import print_function #Allows python3 use of print()

I will be calling some random numbers, so I need to import the random library.

import random

Now we create a few variables which we will use later in the program. At this stage we will just create them, and will discuss where they are used later in the blog.

#Variables used through out the program
noSwap = 0
swap = 0
winCountNoSwap = 0
loseCountNoSwap = 0
winCountSwap = 0
loseCountSwap = 0
numberOfTests = 1000000 #Alter this to change the number of simulations

Now the first thing we said we would do was to set up a blank game, with three doors. Two doors should have a goat behind them, and one a car. These should be randomly selected.

As setting up a blank game is something we will have to do before each of the simulations we run, I have put this code in a function to allow me to call it many times. The function in its entirety is here, afterwards we will run through it line by line.

##Function to set up the game.
##Returns list with three items. 1 Car and 2 Goats randomly positioned.
def setUpGame():
number = random.randint(0,2) #random number 0,1 or 2
blankDoors = [0,0,0] #sets up a new game
count = 0
for item in blankDoors:
if count == number:
blankDoors[count] = 'Car'
else:
blankDoors[count] = 'Goat'
count += 1
return blankDoors

The first line sets the function name, and as the brackets are empty explains we are not sending any variables into the function.

def setUpGame():

Now we know we want a car behind one door and a goat behind two. Therefore if we create a list holding the three things, one of these will be a car.

Lets generate a number from 0 - 2 to decide which item in the list will be the car. You might be asking why not a number from 1-3, as there are three doors? Well lists in Python, along with most other languages, have the first item in position 0. So door 1 is at position 0 in the list. Therefore our random number needs to be a 0, 1 or a 2.

number = random.randint(0,2) #random number 0,1 or 2

Now we will create a list with three items in it, for now these are all set to 0.

blankDoors = [0,0,0] #sets up a new game

We want to assign either goat or car to each of the items in the list. So, as we iterate through the list, lets keep count of where we are up to. If our count is equal to the random number we calculated, which covers only one of the locations in the list, then that should be the car, otherwise a goat should be assigned to the slot.

So first make a count variable. As we iterate through the list this keeps track of the position in the list we are up to, either a 0, 1 or 2.

count = 0

This next line iterates through each of the items in the list one at a time.

for item in blankDoors:

We want to ensure if the count is equal to the random number we generated, this item in the list should be assigned as the car. First the check to see if the count is equal to the random number we generated earlier.

if count == number:

If the count is equal to the number, then this position in the list should be turned into Car.

blankDoors[count] = 'Car'

If it isn't, then it should become a Goat.

else:
blankDoors[count] = 'Goat'

Well we have now done everything we want to with that item in the list, so all that remains is we update the count ready to match the next items position in the list we are going to be looking at.

count += 1

Notice to increase the count by one we use += 1. We could say count = count + 1, but as this is done often in Python there is a shortcut method of typing this as count += 1. If we wanted to remove 1 from count we could say count -= 1.

Finally once we have finished iterating thorough the list, we want to return our now populated list back to the main program. We do this with

return blankDoors

So now if we call this setUpGame() function, we will be returned a blank game. Perfect.

The next thing on our list was to simulate Monty Hall revealing a goat in one of the doors the contestant has not chosen.

For simplicity we are going to assume the contestant always chooses door 1. As the Goats and Cars are assigned randomly it really doesn't matter which door we say the contestant has chosen. This just makes our code easier for the simulations.

As we will want Monty Hall to reveal a goat in all our games, I have written this part as a function, so we can easily call it as many times as we want.

Here is the overall function, then we will break it down to see what it does.

##Function to simulate Monty Hall revealing a goat.
##Assumes contestant has door one.
##Reveals only a Goat in either door 2 or 3.
def reveal(currentGame):
randomReveal = random.randint(1,2)
if currentGame[randomReveal] == 'Car':
if randomReveal == 1:
randomReveal = 2
else:
randomReveal = 1
currentGame[randomReveal] = 'Reveal'
else:
currentGame[randomReveal] = 'Reveal'
return currentGame

First of all we set the name of the function. You can see we are passing the currentGame into it. This means the function has access to currentGame, and can make any modifications required.

def reveal(currentGame):

In the next line we create a random number which is either a 1 or a 2. We have stated that the contestant will always choose door 1, which is in position 0 in the list. We we want to ensure Monty Hall reveals either door 2 or 3, which are held in the list in position 1 or 2.

randomReveal = random.randint(1,2)

We only want Monty Hall to reveal a goat. The next five lines look into if the random number matches a door which is hiding the car, which is a scenario we don't want. If this happens we get Monty Hall to switch the door he is about to reveal.

First of all we look to see if the random number (stored in randomReveal) we have just created matches the location of the car.

if currentGame[randomReveal] == 'Car':

If it is a car behind the random door we want Monty Hall to reveal the other door. Therefore if he was going to reveal door 2 and this is a Car we want him to reveal door 3 which will be a goat and vice versa. The next few lines simply switch the randomReveal number to select the other position in the list. This is done by having if and else statements indented within the if statement above.

if randomReveal == 1:
randomReveal = 2
else:
randomReveal = 1

Notice that the else covers all other eventualities. In this case if randomReveal is not 1, it must be 2, in which case we want to set randomReveal to 1.

Now so we know that door has been revealed let us change what is behind the door to say Reveal instead of Goat.

currentGame[randomReveal] = 'Reveal'

All that was to deal with the fact the Monty Hall was going to reveal a Car. If the randomReveal number was going to cause Monty Hall to reveal a goat, then thats ok. We will simply turn the goat behind that door into Reveal. Note that this else statement is at the same indentation level as the first if statement in the function. It is really important that you get the indentation right, as your whole program can change if it is wrong!

else:
currentGame[randomReveal] = 'Reveal'

Finally we return the modified version of current game which has one of the doors revealed.

return currentGame

The next thing we said we had to do was to get the player to swap doors. Now this is not something which they player needs to do every-time, as we want to compare swapping with not swapping doors. But we do want our program to have the ability to simulate the player swapping doors when Monty Hall gives us the option.

Again this is something we will need to do often in our simulation, so it is written in a function.

##Simulates the player swapping doors.
##Ensures they do not pick the 'Reveal' door.
def swapOption(currentGame):
temp = currentGame[0]
if currentGame[1] == 'Reveal':
currentGame[0] = currentGame[2]
currentGame[2] = temp
else:
currentGame[0] = currentGame[1]
currentGame[1] = temp
return currentGame

The first line defines the function name and explains we are passing currentGame into the function.

def swapOption(currentGame):

The way we will do the swap may be a little different than you think. However it is to make our program a little easier. Rather than swap doors we have chosen, we will simply swap what is behind the doors. So the contestants door will still remain door 1, but we will swap what was behind door 1 with whatever is behind the door which Monty Hall has not revealed to us.

The first thing we will do is to store what is behind the contestants door in a temporary variable called temp.

temp = currentGame[0]

We will use this to replace what is behind the door Monty Hall has not revealed in a minute or two.

What we are trying to do is choose which door has not been revealed to be a goat by Monty Hall, and switch that one with the contestants door, which is door 1. We first check to see if door 2 (stored in position 1 in the list remember) is Reveal

if currentGame[1] == 'Reveal':

It is is, we want to swap what is behind door 3 (stored in the list in position 2) with what is behind the contestants door. This line takes what was behind door three and puts it in the position of the contestants door.

currentGame[0] = currentGame[2]

Now we need to take what was behind the contestants door and put this behind door 3. Hold on a second! We have just over written what was behind door 1. Luckily we made a copy by storing a copy in temp. So we simply copy what is in temp to the position in the list which is storing what is behind door 3.

currentGame[2] = temp

We know Monty Hall has revealed either door 2 or door 3. Therefore if Door 2 is not 'Reveal' he must have revealed door 3. We use an else statement to deal with this scenario and then swap what is behind door 2 (position 1 in the list) with door 1 (position 0 in the list), using the same method as above.

else:
currentGame[0] = currentGame[1]
currentGame[1] = temp

Finally we return the values in currentGame which now reflect the player accepting the swap.

return currentGame

Now we enter a while loop to run through a simulation of how many times you will win or lose if you do not swap doors when offered by Monty Hall.

The first thing we do is start the while loop. We are going to need to use a few of those variables we created at the top of our program at this point. The whole of the while loop looks like this.

##Runs through the game without swapping the door chosen.
while noSwap < numberOfTests:
currentGame = setUpGame()
#print (currentGame)
currentGame = reveal(currentGame)
#print (currentGame)
if currentGame[0] == 'Car':
winCountNoSwap += 1
else:
loseCountNoSwap += 1
noSwap +=1

The first line states that while noSwap is less than the numberOfTests the loop should keep going. This ensures that we run the tests as many times as stated in the numberOfTests variable. We could say while noSwap < 1000000: However in a few months time, it would be hard to remember what the 1000000 was used for. It is also harder to find it in the body of the program rather than at the top. We would also have to change it in a couple of locations.

while noSwap < numberOfTests:

The next thing we do is call our setUpGame function and store the result in currentGame.

currentGame = setUpGame()

There are some print statements in this while loop which I have commented out. I have left these in there to show you that when debugging I will often print the outputs out to ensure they are working as expected. However this slows the simulation, so I comment them out when running a real simulation.

Monty Hall then wants to reveal a door which has a goat, and ask if you want to swap. We will have Monty Hall reveal the goat, but in this simulation you are not swapping.

currentGame = reveal(currentGame)

Now we start to check if the contestant has won. We know they will have won if what is behind their door is a car, unless of course they always wanted a goat! We have ensured that the contestants door is always door 1. And rather than swapping doors, we swapped what was behind the doors. As the item in position 0 in our list represents what is behind door 1 we can easily check if this is a car.

if currentGame[0] == 'Car':

If it is the contestant has won! We will use another of those variables we created at the start to keep track of how many times the contestant has won

winCountNoSwap += 1

If they have not won then we keep track of the losses as well.

else:
loseCountNoSwap += 1

Finally we need to increase our count of how many simulations we have done otherwise our while loop will run forever.

noSwap +=1

Well that was the simulation for if we do not want to swap when offered by Monty Hall. We now do something similar for the option when we do want to swap when offered. Again we do this in a while loop.

##Runs through the game swapping the door chosen.
while swap < numberOfTests:
currentGame = setUpGame()
#print (currentGame)
currentGame = reveal(currentGame)
#print (currentGame)
currentGame = swapOption(currentGame)
#print (currentGame)
if currentGame[0] == 'Car':
winCountSwap += 1
else:
loseCountSwap += 1
swap +=1

This while loop is incredibly similar apart from two things.

The variables have different names to reflect the fact that we are swapping. This means we don’t write over the information created in the previous while loop.

We have an extra line in there to call the function which carries out the swap, to make the swap when offered. We wrote this function earlier. This line is.

currentGame = swapOption(currentGame)

Now we have all the data which we need to work out which is the better option, all we need to do it to calculate it and print it. We do that in the next two lines.

##Calculates percentage of winning for each of the options and prints the result.
print ("Without swapping you win" ,float(winCountNoSwap) / (float(winCountNoSwap) + float(loseCountNoSwap))*100, "% of the time.")
print ("With swapping you win" ,float(winCountSwap) / (float(winCountSwap) + float(loseCountSwap))*100, "% of the time.")

These lines print some text first of all, saying “Without/With swapping you win.”

They then use the results we have gathered to calculate the percentage of winning.

Using this equation

__ Wins __ x 100%

Wins + Losses

(Wins + Losses) is used to add up the total of simulations done.

But what is float for? Well in programming numbers can be stored as different things. All our numbers are stored as int which is short for integer. An integer is a whole number. However in Python if you do any maths on an int, the result will be an int also. Before we multiply our answer by 100 to get a percentage we will have a number less that 1, this will be rounded down to 0. Which is no good to use. To change our numbers to floating point, using float, ensures have decimal points. Carrying out maths on values which are floats will give us the answer as a float.

The final part of each line is to finish off the text with ‘% of the time.’

If you are interested in the results from this program, I can provide some below. I ran this program with the number of samples set to 1,000,000,000. Yes you read that correctly. I simulated 1 Billion game shows! Which really did not take long! The results are:

Without swapping you win 33.3321941 % of the time.

With swapping you win 66.6647868 % of the time.

I hope you have enjoyed this blog post. What I learned from writing it is this is a fairly complex problem to think about, and it has fooled many people, including some top mathematicians. Although I would struggle to prove the answer from a mathematical point of view, this was a simple thing to solve in Python.