Monty Hall Problem

In C++, Programming by timfanelliLeave a Comment

I read a very interesting article on the NY Times today, linked from this /. article, about the Monty Hall Problem. The problem has apparently caused much embarrassment among mathematicians, and now psychologists, and it’s a very interesting read.

I couldn’t quite agree with the argument put forth, and I’m no mathematician, so I wrote a simple program to calculate some numbers for me… first, lets review the problem at hand:

  1. I present you with three closed doors. Behind one is a new car, the other two, a goat.
  2. You choose one of the three doors, hoping it has a car.
  3. I open one door exposing a goat.
  4. You may keep your original door, or change your mind choosing the other closed door.
  5. Open your door, if it’s the car, you can keep it.

So as impulsive humans, it’s easy to see, and think, that since I open one door, your final choice gives you a 50/50 shot at winning a car. The argument in the paper those posits that you should change your mind, since your original decision only had a 33% chance of being correct, and a 66% chance of being wrong.

The source for this simulation is included below. It very closely follows the steps I laid out above, resetting the doors at the start of the loop, randomly choosing which one contains a “car” (true value). The simulates a user’s choice (rand mod three), opens a door containing a goat, and then simulates the user randomly choosing to change their mind (rand mod 2). It totals how many times the user kept their original door and won a car, as well as how many times the user changed their minds and won a car.

I present the results here for you to mull over – I ran 75,000,000 iterations of the loop and got the following:

How many iterations: 75000000
In 75000000 runs, we won a car 44.4418% of the time.
	Used original choice and won a car 12499628 out of 37505245 times... 33.3277%
	Chose the other door and won a car 20831722 out of 37494755 times... 55.559%

Suggesting that your original choice is only correct 33% of the time – which makes perfect sense. However, after I open a door and narrow your choices down, you have about a 50% chance of winning the car when you change your mind.

Since your original choice was made out of three closed doors, you’ll win with that choice 33% of the time. However, when you change your mind you’re only choosing out of two closed doors, so you’ll win about 50% of the time.

Doesn’t add up, I know – but makes a certain bit of sense.

/**
 Monty Hall's game simulator… 
 Calculates win percentages when you keep your original door 
 or change your mind in a monty hall problem - 
    http://en.wikipedia.org/wiki/Monty_Hall_problem
 *
 Author: Timothy C. Fanelli
 Date: April 10, 2008
 **/ 
 include <cstdlib>
 include <cassert>
 include <ctime>
 include <iostream>
 using namespace std;
 int main() {
   int iterations;
   bool contents[] = { false, false, false };

   cout << "How many iterations: ";
   cin >> iterations;

   int changedDoorCount = 0;
   int correct[] = {0, 0};

   srand( time( 0 ) ); 
   for ( int i = 0; i < iterations; ++i ) {
     int goatOne, goatTwo;
     int carIdx = rand() % 3;
     // Setup the monty hall's doors: 
     contents[0] = false;
     contents[1] = false; 
     contents[2] = false; 
     contents[carIdx] = true; 
     if ( carIdx == 0 ) {
       goatOne = 1;
       goatTwo = 2; 
     }
    else if ( carIdx == 1 ) {   
       goatOne = 0;
       goatTwo = 2; 
    }  
    else {   
       goatOne = 0;
       goatTwo = 1;
     } 

    // So: 
    //   contents[carIdx]  == true 
    //   contents[goatOne] == false 
    //   contents[goatTwo] == false 
    assert( contents[carIdx] ); 
    assert( ! contents[goatOne] ); 
    assert( ! contents[goatTwo] ); 

    // Simulate an initial choice, then expose a goat: 
    int userChoice = rand() % 3; 
    int doorExposed = (goatOne==userChoice)?goatTwo:goatOne; 

    // Do we change doors? 
    bool changedDoors = rand() % 2; 
    if ( changedDoors ) {   
        changedDoorCount++;   
        // Pick the door that isn't the user's choice    
        // and isn't the exposedDoor... note:    
        //   
        // userChoice != doorExposed by design.   
        if ( userChoice == 0 )    
        { 
            if ( doorExposed == 1 )
               userChoice = 2; 
            else
               userChoice == 1;
        }
        else if ( userChoice == 1 )
        { 
            if ( doorExposed == 0 )
                userChoice = 2; 
            else
                userChoice = 0;
         }
         else   
         {
             if ( doorExposed == 0 )    
                userChoice = 1; 
             else    
                userChoice = 0;   
         }   
         assert( userChoice != doorExposed ); } 

         // Track how many times we chose correctly - 
         //   Increment correct[0] if we kept our original choice 
         //   Increment correct[1] if we chose the other door. 
         if ( contents[userChoice] ) {   
           correct[ changedDoors ]++; 
         }
    }
 
    double changedWinPercent = 
               100(correct[1]/static_cast(changedDoorCount));
    double originalWinPercent = 
              100(correct[0]/static_cast(iterations-changedDoorCount));
      double totalWinPercent = (correct[0]+correct[1]) / static_cast( iterations ) * 100;
 cout << "In " << iterations << " runs, we won a car " << totalWinPercent << "% of the time." << endl;
 cout << "\tUsed original choice and won a car " << correct[0] 
        << " out of " << iterations - changedDoorCount << " times… " 
        << originalWinPercent << "%" << endl;
 cout << "\tChose the other door and won a car " << correct[1] 
        << " out of " << changedDoorCount << " times… " 
        << changedWinPercent << "%" << endl;
 }

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.