I dunno if this will help but a while back when teaching a computer class I explained the Monty Hall problem to my students and asked them what they thought. They all replied that switching didn’t matter. I then gave them a homework assignment to write a computer program to simulate the problem and run it thousands of times. Afterwards every single one of them understood how it worked because writing the program showed how the problem could be broken down. I’ve tried to illustrate the steps using multiple Ruby programs.
The first one duplicates all the steps of the Monty Hall problem:
[ul][li]Program sets up three doors and randomly chooses the “winning” door.[/li][li]Contestant randomly chooses a door.[/li][li]Computer randomly chooses one of the other doors for Monty to open.[/li][li]If Monty’s random door is the winner, then switch Monty’s door to the last door.[/li][li]Displays the results.[/li][li]Determines if contestant wins if she stays with the original choice.[/ul][/li]The program runs this 1000 times and displays the win percentage if the contestant doesn’t switch. (I tried to stay away from ruby short-cuts to make it easier for those who don’t know ruby.)
stay = 0
trials = 1000
# Run the test 'trials' number of times.
(1..trials).each do
# Initialize doors so all are empty.
doors = []
doors[0] = false # First door
doors[2] = false # Second door
doors[3] = false # Third door
# Randomly choose one door to have the winner.
winner = rand(3)
doors[winner] = true
# contestant randomly chooses a door.
contestant = rand(3)
# Monty chooses a random door to open.
random_door = rand(2) # Monty will choose between one of two doors.
monty = ( contestant + # Take the contestant's door...
1 + # Add one so Monty doesn't choose contestant's door...
random_door ). # Add the random door number that Monty chooses...
modulo(3) # Then take modulo 3 to make sure door # is valid.
# If the random door Monty chose is the winner,
# then switch Monty's door to the other, non-contestant, door.
if doors[monty] == true
# Oops, Monty chose the winner. Switch to the other door.
if random_door == 0
monty = (contestant + 2).modulo(3)
else
monty = (contestant - 1).modulo(3)
end
end
# Print the set-up:
puts "Winner is door #{winner}, contestant chose #{contestant}, Monty opened #{monty}"
# Sanity check: make sure Monty's door isn't the winner or the contestant's
if monty == contestant or monty == winner
puts "** DANGER DANGER WILL ROBINSON**"
return 1
end
# Determine if switching or staying wins:
if winner == contestant
stay = stay + 1
end
end
puts "contestant won #{stay.to_f / trials * 100}% of the time if she didn't switch"
If you run this program the results are always around 33% (in my tests it has been as low as 30% and as high as 37%).
The first simplification we can make is noting that it doesn’t matter if Monty randomly chooses one of the two non-contestant doors. We can change the logic so that Monty always opens the “next” door (unless it’s the winner, then he’ll chose the one after that.) The slightly simplified version looks like this:
stay = 0
trials = 1000
# Run the test 'trials' number of times.
(1..trials).each do
# Initialize doors so all are empty.
doors = []
doors[0] = false # First door
doors[2] = false # Second door
doors[3] = false # Third door
# Randomly choose one door to have the winner.
winner = rand(3)
doors[winner] = true
# contestant randomly chooses a door.
contestant = rand(3)
# Monty chooses the door after the contestant's to open.
monty = ( contestant + 1).modulo(3)
# If the door Monty chose is the winner, then choose the next one
if doors[monty] == true
monty = ( monty + 1).modulo(3)
end
# Print the set-up:
puts "Winner is door #{winner}, contestant chose #{contestant}, Monty opened #{monty}"
# Sanity check: make sure Monty's door isn't the winner or the contestant's
if monty == contestant or monty == winner
puts "** DANGER DANGER WILL ROBINSON**"
return 1
end
# Determine if switching or staying wins:
if winner == contestant
stay = stay + 1
end
end
puts "contestant won #{stay.to_f / trials * 100}% of the time if she didn't switch"
Running this results in the same 33.% win rate if the contestant doesn’t switch. The next simplification we can make is getting rid of the ‘doors’ array. The only time it’s needed is when we execute “if doors[monty] == true” and we can change that to “if monty == winner”. Here’s the third revision (with some ruby simplifications as well):
stay = 0
trials = 1000
# Run the test 'trials' number of times.
(1..trials).each do
# Randomly choose one door to have the winner.
winner = rand(3)
# contestant randomly chooses a door.
contestant = rand(3)
# Monty chooses the door after the contestant's to open.
monty = ( contestant + 1).modulo(3)
# If the door Monty chose is the winner, then choose the next one
monty = (monty + 1).modulo(3) if monty == winner
# Print the set-up:
puts "Winner is door #{winner}, contestant chose #{contestant}, Monty opened #{monty}"
# Sanity check: make sure Monty's door isn't the winner or the contestant's
if monty == contestant or monty == winner
puts "** DANGER DANGER WILL ROBINSON**"
return 1
end
# Determine if switching or staying wins:
stay = stay + 1 if winner == contestant
end
puts "contestant won #{stay.to_f / trials * 100}% of the time if she didn't switch"
Now comes the surprising simplification. Note that after the following line
monty = (monty + 1).modulo(3) if monty == winner
we don’t use the variable ‘monty’ (except to print the intermediate state and the sanity check). Monty’s choice isn’t used when determining if the contestant should switch or not! This means that we can remove all the references to the ‘monty’ variable without changing the final results. Here’s the new program:
stay = 0
trials = 1000
# Run the test 'trials' number of times.
(1..trials).each do
# Randomly choose one door to have the winner.
winner = rand(3)
# Contestent randomly chooses a door.
contestent = rand(3)
# Determine if switching or staying wins:
stay = stay + 1 if winner == contestent
end
puts "Contestent won #{stay.to_f / trials * 100}% of the time if she didn't switch"
Again, running this gives us the same 33% win rate.
This means that what Monty shows the contestant is meaningless when determining whether she should switch or not. One way this can be explained is that Monty is showing the contestant that at least one of the other doors is not the winner but the contestant already knew that.
We can make one more simplification:
stay = 0
trials = 1000
# Run the test 'trials' number of times.
(1..trials).each do
# Determine if switching or staying wins:
stay = stay + 1 if rand(3) == rand(3)
end
puts "Contestent won #{stay.to_f / trials * 100}% of the time if she didn't switch"
The last program is logically performing 1000 runs of the Monty Hall program. (Other simplifications can be made but at this point they are trivial.)