Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Monty Hall Paradox Illustrated

4.59/5 (15 votes)
27 Oct 2008CPOL5 min read 1   173  
A Window Forms application illustrating the Monty Hall paradox.

If your experiment needs statistics, you ought to have done a better experiment.

- Ernest Rutherford

What is Monty Hall paradox?

In case you have watched the movie "21", you may remember a scene in the beginning of the film when a lecturer confronts his student with the following logical puzzle:

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, and the host who knows what's behind the doors opens another door which has a goat. He then asks if you want to rethink and choose another door. Is it to your advantage to switch your choice?

The student agrees to swap his choice, and he is right. His winning chances will then increase from 1/3 to 2/3. The extract from the movie can be viewed here, and Wikipedia has a fairly comprehensive explanation of this paradox known as Monty Hall problem, named after a television show host.

I will not repeat the solution of the problem here. It’s well explained in various sources available on the net. Still, the paradox triggers hot discussions when sometimes theoretical argumentation is not enough - people may insist that such things just can’t happen.

This is why I decided to write a simple Windows Forms application that takes as input the number of doors (let’s show the statistics for any number of doors, not just three, and the host will have to open N-2 doors with goats behind them) and the number of iterations, and shows the chances of a player that chooses to swap his original choice. For clarity, we will assume that the player chooses to switch his initial choice, so all chances and probabilities will be computed with regards to the strategy based on choice switching.

So much depends on the host!

To make a program more illustrative, I decided to show not just Monty Hall’s case. Since so many people insist that swapping the door does not increase the player’s chances, I wanted to show when it is actually true: and it is true if the host opens doors randomly, with a risk to reveal the car. But, if the host knows where the car is and deliberately opens only doors with goats, then swapping the door is in the player’s interest – and the greater the number of the doors is, the higher the winning chances after swapping.

Well-informed host

Let’s start now with the classical Monty Hall case: when the host knows what’s behind the doors and only open doors with goats behind. Let’s make random selections for a lucky door and the player’s initial choice.

C#
int luckyDoor = random.Next(0, numberOfDoors);
int doorSelectedByPlayer = random.Next(0, numberOfDoors);

Now, we should make a selection for the host, shouldn’t we? Well, actually we don’t need to. He has not so much freedom to make a selection, and even when he has a choice, nothing really depends on it. If the player didn’t pick up a door with the car behind it, then the host will have to open all the remaining doors with goats, leaving unopened the door with the car. In this case, swapping our first choice will make the player a winner. But, if the player had luck in his first attempt, that was his bad luck, because as we mentioned earlier, we evaluate the strategy when the player switches his initial choice; so, no matter what door(s) the host selects to open, the player will choose another door and reveal the goat. Here’s how the algorithm will look for the informed host:

C#
if (luckyDoor == doorSelectedByPlayer)
{
    // Player guessed right on the first attempt,
    // so he will lose after the swap
    return GameResult.Goat;
}
else
{
    // Player missed on the first attempt,
    // so he will win the car after the swap
    return GameResult.Car;
}

Clueless host

The case with a clueless host is somewhat more complicated, since what can happen is that the host randomly picks a door with the car behind it. Obviously, such a situation breaks the game – we are evaluating the winning chances in a situation when the host reveals the goats. So, the rules of the game with a clueless host require the game to be replayed in case the car is revealed while the host opens N-2 doors that are not selected by the player. In this case, unlike the classical Monty Hall situation, the host’s selection must be taken into account:

C#
int luckyDoor = random.Next(0, numberOfDoors);
int doorSelectedByPlayer = random.Next(0, numberOfDoors);
int doorNotSelectedByHost = random.Next(0, numberOfDoors - 1);

if (luckyDoor == doorSelectedByPlayer)
{
    // Player guessed right on the first attempt,
    // so he will lose after the swap
    return GameResult.Goat;
}
else
{
    // Player missed on the first attempt, check if host opened the lucky door
    if (luckyDoor < doorSelectedByPlayer && luckyDoor != doorNotSelectedByHost ||
        luckyDoor > doorSelectedByPlayer && luckyDoor != doorNotSelectedByHost + 1)
    {
        return GameResult.Discarded;
    }
    else
    {
        // Player will win the car after the swap
        return GameResult.Car;
    }
}

Let’s look closer at the code above. In case the player initially selects a door with the car, he will again end up with a goat. The situation when he first selects a door with a goat needs further examination. Instead of selecting N-2 doors to be open by the host, we simply select one of the remaining (unselected) N-1 doors not to be open. It is equivalent. The host may not open a door selected by the player, so we need to implement a "hole" in his door set that corresponds to a player’s door. The game result is discarded (and the game is replayed) when the car is behind one of the doors open by the host (i.e., luckyDoor != doorNotSelectedByHost). If the game is not discarded, then the player will become a winner after swapping his initial choice.

Notes on random number generation and threading

As much as I like to implement self-contained classes (especially in such a simple example), I had to create an instance of a Random object and use it in classes that implemented different host strategies (GameWithInformedHost and GameWithCluelessHost). Creating a random generator in each iteration was too slow, and resulted in less randomized data (because of repetitive use of the same seed). Sharing a single instance of the Random object between classes made the application run much faster.

Originally, I considered running simulated games in multiple threads with continuous progress report, but after testing how blazingly fast is to run such algorithms on modern machines, I concluded that multi-threading would only introduce additional code complexity without any gain in practice. Running a million games take only a few seconds, and is enough to produce results with three significant digits.

Million Monty Hall games at once

The Monty Hall application simulates any number of iterations with any number of doors. Here are the results produced with three doors and ten million iterations:

MontyHall

And, here are the results if the car is behind one of hundred doors:

MontyHall

As you can see, in the case of well-informed host, increasing the number of doors makes swapping the player’s initial choice a must: it’s all or nothing. And, if the host is clueless, the player’s chances are still 50/50.

References

  1. Monty Hall Problem
  2. 21, the movie

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)