Answer to “The Goats and the Corvette”

(b) It’s always better to switch.  Yes, really.  The explanation is below, followed by a simulation that demonstrates it.


If you don’t switch, you have a 1 in 3 chance of winning the Corvette; if you do switch, you have a 2 in 3 chance.

Remember, there is an important hint in the formulation of the problem.  The game show host is never going to open the door containing the car.  He’s always going to show you a goat, eliminating one of the bad choices.  Does that help?

”But,” you may say, “you might have picked right the first time.”  Right.  I think this kind of objection reveals the way our brains work; we feel like the “switcher” is throwing away a bird in the hand, that is, we assume that the first choice was a good one.  But it is actually likely to be a bad one.  2/3 of the time, you’ll have picked a goat as your first choice.  And now figure this:  if you have made a bad first choice, your second choice is a guaranteed win.  You’ll be sitting on a goat, and the game show host will reveal the other one.  The only door left is the car!

The only way for the switcher to lose is to have made a good first choice, something that happens only 1/3 of the time.

If you think this is a bunch of hand-waving, try the simulation below.


This is a program written in Unicon (formerly Icon), a programming language created by Dr. Ralph Griswold at the University of Arizona.  If you don’t want to download Unicon and try it on your own computer, I think the code and the language are readable enough that you can transliterate it into your favorite language, using the guide at the end.

Here’s some sample output, from a couple of runs.

How many iterations? 100
Switcher:  78 cars;  Stayer:  22 cars
How many iterations? 1000
Switcher:  694 cars;  Stayer:  306 cars
How many iterations? 10000
Switcher:  6717 cars;  Stayer:  3283 cars

The entire program:

# let’s make a deal

global NDoors

procedure none_the_same (n1, n2, n3)
  return n1 ~= n3  &  n2 ~= n3

procedure main ()

  NDoors := 3

  writes(”How many iterations? “)
  iterations := numeric(read())

  switcher := stayer := 0
  every i := 1 to iterations do {
    car := ?NDoors			# Where the car is
    choice1 := ?NDoors		# player’s first choice
    until dealers_choice := none_the_same(car, choice1, ?NDoors)
    choice2 := none_the_same(choice1, dealers_choice, 1 to NDoors)
    # OK!  Who won?
    if car = choice1 then
      stayer +:= 1
    else if car = choice2 then
      switcher +:= 1
  write(”Switcher:  “, switcher, “ cars;  Stayer:  “, stayer, “ cars”)


Guide to Icon

If Unicon is unfamiliar, here is a quick glossary:

Programmers in other languages may be confused by the line

choice2 := none_the_same(choice1, dealers_choice, 1 to NDoors)

as it contains a loop construct within a function call.  What it means is “try 1 through NDoors as arguments to ‘none_the_same’ until one of them succeeds”.  An equivalent way to write it would be

every i := 1 to NDoors do {
  if none_the_same(choice1, dealers_choice, i) then {
    choice2 := none_the_same(choice1, dealers_choice, i)

The first way is known as “goal-directed evaluation” and is one of the features of Icon that make it so expressive.

The body of the none_the_same function may also be confusing.  It appears to be returning a boolean, but it isn’t; it’s returning n3 if it succeeds.  Icon has a concept of “failure” whose closest analogue in C++ or Java would be an exception.  Think of the ~= (numerically not equal) operator as throwing an exception if its operands are equal but returning its right-hand operand if they are not.  In other words, in Java, it might look like

int none_the_same (int n1, int n2, int n3) throws equal_after_all
  if (n1 != n2  &&  n2 != n3)
    return n3;
    throw equal_after_all;

What makes failure nicer than exceptions in a program like this is how you can put it to work for you, as is done with the expression 1 to NDoors as an argument to the function.  As long as the expression fails, any generators within it continue “trying” values.