Simulating Chance

Checking the Monty Hall Answer with a Simulation

Outline a short program that plays the Monty Hall game many times, and see why switching doors wins about two thirds of the time while staying wins about one third.

On this page
  1. The game to be coded
  2. Decide what is random
  3. A short program
  4. What the output should look like
  5. Why the simulation agrees with the argument
  6. Changing the rules to test your understanding
  7. Cautions

The game to be coded

In the Monty Hall game there are three doors. A prize is behind one and nothing valuable is behind the other two. You pick a door. The host, who knows where the prize is, then opens one of the other two doors, always one without the prize. You are offered a choice: stay with your door or switch to the remaining closed one.

The accepted answer is that switching wins with probability 2/3 and staying wins with probability 1/3. Many people find this hard to believe, and a simulation is a good way to see it without relying on anyone’s argument. The reasoning itself is covered in the Monty Hall problem explanation; here the goal is to build a program that tests it.

Decide what is random

The simulation needs to model three random or rule-driven things:

  1. Where the prize is, chosen uniformly from the three doors.
  2. Which door the player picks first, also uniform.
  3. Which door the host opens, which is not random in the usual sense: it must be neither the player’s door nor the prize door. If the player picked the prize door, the host has two valid choices and picks one; otherwise only one door is valid.

Getting the third item right is the whole point. If you let the host open any door at random, you have simulated a different game.

A short program

Here is one way to write it in Python:

import random

def play(switch):
    doors = [0, 1, 2]
    prize = random.choice(doors)
    pick = random.choice(doors)

    # Host opens a door that is not the pick and not the prize.
    options = [d for d in doors if d != pick and d != prize]
    opened = random.choice(options)

    if switch:
        pick = [d for d in doors if d != pick and d != opened][0]

    return pick == prize

trials = 100_000
stay_wins = sum(play(False) for _ in range(trials))
switch_wins = sum(play(True) for _ in range(trials))

print("stay:  ", stay_wins / trials)
print("switch:", switch_wins / trials)

The function plays one full game and returns whether the player ends on the prize. The two totals are then divided by the number of trials.

What the output should look like

With 100,000 games for each strategy, the staying figure typically comes out near 0.333 and the switching figure near 0.667. They will not be exact, and they will change slightly each time you run the program.

How close should they be? For a true chance of 2/3 over 100,000 trials, the standard error is the square root of (2/3 x 1/3 / 100,000), which is about 0.0015. So results within roughly 0.003 of 0.667 are entirely ordinary. If you ran only 30 games you would see far more scatter, and might even see staying win more often than switching in that small sample. That is a useful lesson in itself about the number of trials a simulation needs.

Why the simulation agrees with the argument

The code makes the logic visible. Staying wins only when the first pick was the prize door, and that happens one time in three. Switching wins in every other case, because the host has already removed the only other losing door, leaving the prize behind the door you switch to. The program counts exactly this, just by brute repetition.

You can also tally the games by first pick. In the roughly one third of games where the first pick was the prize, switching loses. In the other two thirds, switching wins. Probability trees for the game show the same split on paper, and the simulation is a way to confirm you drew them correctly.

Changing the rules to test your understanding

A good way to learn from the code is to break the rule and see what changes.

  • Host opens a random other door. Suppose the host does not know where the prize is and opens one of the two other doors at random. Games where the host happens to reveal the prize are thrown away, and among the remaining games switching and staying each win about half the time. The difference arises because the host’s action now carries different information, which is a point about conditional probability.
  • More doors. Use ten doors, with the host opening eight losing doors after your pick. Staying should win about one time in ten and switching about nine times in ten. This is often what makes the intuition click.
  • Host favours one door. If the host has a preference when two doors are available, the overall result stays the same, but results conditional on which door was opened can shift.

Each variation takes only a line or two to change, which is the strength of simulation as a learning tool.

Cautions

A simulation like this confirms an answer under a particular set of assumptions. It cannot tell you whether the real situation matches those assumptions, and in the original puzzle the host’s rule is exactly the part people often leave out of the statement. If a puzzle wording is vague, the code forces you to choose, and different choices give different answers.

The numbers are also estimates. Do not read much into the third decimal place, and run the program a few times to see the natural variation. Games of chance with real money behave differently: outcomes are independent between plays, casino games carry a house edge, and a puzzle with a favourable switching strategy does not carry over to them. Gambling can be harmful, and support services exist for anyone who needs them.

The complete guide to simulating chance

Simulating Probability with Simple Code
Read the guide

Keep exploring

Guides from other topics

All topics →