Vito Cassisi – Tech Blog
2Aug/090

Proving a probability paradox

Logic isn't always as obvious as one first thinks...

door


A paradox, by definition, is a contradiction of logic. I was recently introduced to the Monty Hall paradox, a probability driven problem which appears to have an obvious answer, but in reality, is quite sneaky.
The Monty Hall problem originated with the TV show 'Let's Make a Deal', in 1963. It's named after the host Monty Hall, and the problem in it's basic form involved three doors, each with an unknown object behind it. Only one of the doors contains a prize, the other two have an unwanted results - a goat.

The problem starts by asking the player to choose 1 of 3 doors. The host knows which door contains the prize, the player does not. Once a door has been chosen, the host proceeds to open a door which he knows has no prize. At this point, the player is allowed to swap his door with the remaining unopened one. For instance:

monty hall - prob

Each door has 1/3 chance of being the prize (the car). The player than proceeds to choose a door, in this case, we'll say this is door 1.

monty hall 2

The host cannot select a door which contains the prize. Therefore, in this case, he/she must choose door 3.

monty hall 3

Notice how the probability of the second door becomes 2/3. Remember that the player doesn't know that it contains the prize. According to mathematics, the player doubles his chances of winning if they decide to swap. I personally thought that the probability would become 50:50, after all, there's two doors, only one contains the prize.  Naturally, I wanted concrete evidence that swapping would be beneficial in this situation - so I set off to solve the problem the only way I knew how - by programming it!

Developing the algorithm
An algorithm describes the process of which a program will execute. Essentially, we're programming what we know about the Monty Hall problem, and allowing the computer to simulate it an arbitrary amount of times. The player and host decisions are based on a random number generator, to ensure a fair result. The program will then tally the results and provide a ratio. If swapping really does give the user a 2/3 chance of winning, then the ration of 'swap and win' to 'stay and win' should be 2. i.e. 2/3 is double the chance of 1/3.

The algorithm I've chosen is quite simple:

1. Get the number of rounds to perform via user input (between 1 and 9999 inclusive)

2. Randomly generate a door value for the player

3. Randomly generate a door value for the host

4. Check that the host value is not the prize, or the door that the player has chosen.

5. Determine whether the winning door is achieved by staying or swapping. Increment the 'stayWinCounter' and 'swapWinCounter' counters accordingly.

6. Loop between 2 and 5 until the amount of rounds has been met.

7. Print the counter values, and the ratio between said values.

Programming the simulator
C# has been chosen as the programming language for its ease of use. Using Visual C# Express Edition 2008, the graphical user interface (GUI) was formed via the drag and drop 'design' window.

MHS

The GUI consists of a small window, with three 'PictureBox' controls, labels (the text), a TextBox for user input, and a button to submit said input. Applications such as these are event driven, that is, code is executed when a user interacts with controls within the program. In this case, the program reacts when the TextBox content is changed, and when the 'GO' button is pressed.

You can download the project file for the program here. Open it within Visual C# Express Edition to view its contents.

Code to react to the 'GO' button:

private void button1_Click(object sender, EventArgs e)
{
    Random Door = new Random();

    int i;
    int stayWinCounter = 0;
    int swapWinCounter = 0;
    double ratioVal = 0;
    int numRounds = Convert.ToInt32(noRounds.Text);

    stayWin.Text = Convert.ToString(0);
    swapWin.Text = Convert.ToString(0);
    ratio.Text = Convert.ToString(0);

    DoorOne.Image = MontyHall.Properties.Resources.door;
    DoorTwo.Image = MontyHall.Properties.Resources.door2;
    DoorThree.Image = MontyHall.Properties.Resources.door3;

    MontyHallSim.ActiveForm.Cursor = Cursors.WaitCursor;

    for (i = 0; i < numRounds; i++)
    {
        int doorWin = Door.Next(1, 4);
        int doorPlayer = Door.Next(1, 4);
        int doorHost = Door.Next(1, 4);

        while (doorHost == doorWin || doorHost == doorPlayer)
        {
            doorHost = Door.Next(1, 4);
        }

        int goatDoor = 6 - doorWin - doorHost;

        if (goatDoor == 1 || doorHost == 1)
        {
            DoorOne.Image = MontyHall.Properties.Resources.doorLose;
        }
        if (goatDoor == 2 || doorHost == 2)
        {
            DoorTwo.Image = MontyHall.Properties.Resources.doorLose;
        }
        if (goatDoor == 3 || doorHost == 3)
        {
            DoorThree.Image = MontyHall.Properties.Resources.doorLose;
        }

        switch (doorWin){
            case 1:
                DoorOne.Image = MontyHall.Properties.Resources.doorPrize;
                break;
            case 2:
                DoorTwo.Image = MontyHall.Properties.Resources.doorPrize;
                break;
            case 3:
                DoorThree.Image = MontyHall.Properties.Resources.doorPrize;
                break;
           default:
                break;
        }

        if (doorWin == doorPlayer)
        {
            stayWinCounter++;
        }
        else
        {
            swapWinCounter++;
        }
        if (stayWinCounter != 0)
        {
            ratioVal = (double)swapWinCounter / stayWinCounter;
        }
    }
    stayWin.Text = Convert.ToString(stayWinCounter);
    swapWin.Text = Convert.ToString(swapWinCounter);
    ratio.Text = Convert.ToString(ratioVal);
    MontyHallSim.ActiveForm.Cursor = Cursors.Arrow;
}



Code to react to the change in contents of the TextBox:

private void noRounds_TextChanged(object sender, EventArgs e)
{
    try
    {
        if (Convert.ToInt32(noRounds.Text) > 9999 || Convert.ToInt32(noRounds.Text) < 1)
        {
            MessageBox.Show("Please enter a number between 0 and 10,000.");
            noRounds.Text = Convert.ToString(9999);
        }
    }
    catch
    {
        MessageBox.Show("Please enter a number between 0 and 10,000.");
        noRounds.Text = Convert.ToString(1);
    }
}



This code will look quite daunting to those who haven't programmed before. It will be explained in detail within the next couple of days. You can see this code in the program by navigating to the control's associated event handler. To do this, double click the Button or TextBox whilst in design view. This will bring you to the appropriate section.

Note: This guide isn't meant to be a programming tutorial as such. However, I highly recommend you muck around with the code and see what you can do with it. The best way to learn is to do!

You can run the program by pressing [F5]. When small values are input, the ratio is quite erratic. There isn't enough 'data' to create an accurate representation of the ratio. With values in excess of 1000, the ratio becomes fairly consistent. And what do you know, the ratio turns out to be 2!

So I was wrong, swapping really does give you a 2/3 chance of winning. For those who want to play with the program direct, without touching the programming side of it, you can get the executable here. Keep in mind that you will need the .Net Framework 3.5 installed to run it.

Next, understanding the code!

http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/digg_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/reddit_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/stumbleupon_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/delicious_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/technorati_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/facebook_24.png http://www.vitocassisi.com/wp-content/plugins/sociofluid/images/twitter_24.png