A little program in RubyLanguage to simulate the MontyHallProblem. We were going to do it in PythonLanguage, but then we'd have to call it MontyPython.
def randomDoor
return rand(3)
end
def otherDoorThan(*forbiddenDoors)
allowedDoors = [0,1,2] - forbiddenDoors
return allowedDoors[rand(allowedDoors.size)]
end
stayingWasRight = 0
changingWasRight = 0
1_000_000.times do
prizeDoor = randomDoor()
firstChosenDoor = randomDoor()
montysDoor = otherDoorThan(prizeDoor, firstChosenDoor)
secondChosenDoor = otherDoorThan(montysDoor, firstChosenDoor)
case prizeDoor
when firstChosenDoor then stayingWasRight += 1
when secondChosenDoor then changingWasRight += 1
else puts "Monty cheated!"
end
end
totalRuns = stayingWasRight + changingWasRight
puts "Absolute Numbers:
Staying was right in #{stayingWasRight}, cases
Changing was right in #{changingWasRight}, cases
Total number of runs: #{totalRuns},
Relative Numbers:
Staying was right in #{100.0 * stayingWasRight / totalRuns},% cases
Changing was right in #{100.0 * changingWasRight / totalRuns},% cases"
A run produced the following output:
Absolute Numbers:
Staying was right in 333810 cases
Changing was right in 666190 cases
Total number of runs: 1000000
Relative Numbers:
Staying was right in 33.381% cases
Changing was right in 66.619% cases
So this supports the notion, that changing is right in 2/3 of the cases. Or can you spot any problems in this program, similar to the problems some of the tables on MontyHallProblem have?
Writing my own MontyHallSimulation made the solution clearer to me. I was able to simplify it to something like this:
prizeDoor = randomDoor();
firstChosenDoor = randomDoor();
if prizeDoor == firstChosenDoor then
stayingWasRight += 1
else
changingWasRight += 1
end
And clearly, prizeDoor == firstChosenDoor only 1/3 of the time.
This is correct, but doesn't really show why the problem is different from NotTheMontyHallProblem. For that, see NotMontyHallSimulation -- AndrewMcGuinness
The only reason you could reduce the problem to above, is because you had done the analysis. So it isn't really a simulation, per se.
Here is a simulation done in lisp. It is somewhat redundant, as no analysis of the choices is done:
(defun monty-hall (N)
"simulate the 'Monty Hall' problem for N iterations"
(defun pick-one (list)
(nth (random (length list)) list))
(let ((stay-success 0)
(switch-success 0))
(dotimes (i N t)
(let* ((doors (list 0 1 2))
(prize-door (pick-one doors))
(your-door (pick-one doors))
(monty-door (pick-one (remove prize-door (remove your-door doors))))
(switch-door (pick-one (remove monty-door (remove your-door doors)))))
(cond ((equal your-door prize-door) (incf stay-success))
((equal switch-door prize-door) (incf switch-success)))))
(list stay-success switch-success)))
and output for a run:
* (monty-hall 100000000)
(33337404 66662596)
A Java applet (with source code) is available here: http://www.rdrop.com/~half/Creations/Puzzles/LetsMakeADeal/monty.hall.applet.html
This applet lets you play the game manually, or the applet can play itself. There are three-door and six-door versions, and running win/lose statistics are displayed as you play.
Here's a version in Perl, loosely based on the Ruby version above, but altered slightly to allow for more than three doors (if desired).
use strict;
use warnings;
my $number_of_doors = 3;
my $total_trials = 1_000_000;
sub random_door {
return int rand $number_of_doors;
},
sub door_other_than {
my @forbidden = @_;
my %doors;
@doors{ 0 .. $number_of_doors - 1 }, = ();
for my $door (@forbidden) {
delete $doors{$door},;
},
my @choose_from = keys %doors;
return $choose_from[ int rand @choose_from ];
},
my $staying_was_right = 0;
my $changing_was_right = 0;
for (1 .. $total_trials) {
my $prize_door = random_door();
my $first_chosen_door = random_door();
my $montys_door = door_other_than($prize_door, $first_chosen_door);
my $second_chosen_door = door_other_than($montys_door, $first_chosen_door);
if ($prize_door == $first_chosen_door) {
$staying_was_right++
},
elsif ($prize_door == $second_chosen_door) {
$changing_was_right++;
},
},
my $stay_percent = 100 * $staying_was_right / $total_trials;
my $change_percent = 100 * $changing_was_right / $total_trials;
print <<END_TEXT;
Total trials: $total_trials
Absolute numbers:
Staying was right in $staying_was_right cases.
Changing was right in $changing_was_right cases.
Relative numbers:
Staying was right in $stay_percent% cases.
Changing was right in $change_percent% cases.
END_TEXT