8
\$\begingroup\$

I've never been able to wrap my head around the Monty Hall problem. Here's the premise:

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, say No. 1, and the host, who knows what's behind the doors, opens another door, say No. 3, which has a goat. He then says to you, "Do you want to pick door No. 2?" Is it to your advantage to switch your choice?

Run 10,000 simulations. Output the win percentage of switching. For example:

> 66.66733%
\$\endgroup\$
9
  • 5
    \$\begingroup\$ The host knows where the car is, so he never opens a door with the car behind it \$\endgroup\$
    – bendytree
    Sep 30, 2013 at 21:19
  • 6
    \$\begingroup\$ As the challenge stands, it's hard to argue that it's not perfectly legal to just have 10,000 runs of the random-number generator and adds up which ones land below 0.6666. You might say that it's not really simulating the Monty Hall problem. But if it produces the exact same output, then what's really missing? \$\endgroup\$
    – breadbox
    Oct 1, 2013 at 0:32
  • \$\begingroup\$ An interesting historical perspective (on the problem, not the golfing): Which Door Has the Cadillac?. \$\endgroup\$ Oct 1, 2013 at 22:29
  • 3
    \$\begingroup\$ The challenge is still a little ambiguous with regards to what assumptions we're allowed to make. For example, some of the answers are probably saving a significant amount of code by assuming the car will be behind the same door every time or the player picks the same door every time. \$\endgroup\$
    – Iszi
    Oct 25, 2013 at 19:09
  • 1
    \$\begingroup\$ Enumerating all the possibilities is at least as illuminating as generating them randomly... \$\endgroup\$ Nov 20, 2013 at 22:03

13 Answers 13

4
\$\begingroup\$

JavaScript 52

for(i=s=0;i++<1e4;)s+=Math.random()<2/3;alert(s/100)

Doors are 1:[0,1/3), 2:[1/3,2/3), 3:[2/3, 1)

Assume the prize is always in door 3. If the guest picks doors 1 or 2, which is range [0,2/3), and they switch, they've won the prize.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Looks like CoffeeScript allows us to shave off a single character: i=s=0;s+=Math.random()<2/3while i++<1e4;alert s/100 \$\endgroup\$
    – Kerrick
    Oct 5, 2013 at 4:55
3
\$\begingroup\$

J: 17 15

100%~+/2>?1e4$3

It picks a random door—let's label these 0, 1, or 2 where 2 is the door with the car—and computes the benefit of switching based on this logic:

  • If the player picked door 0, the host will open door 1. Switching will earn the player a new car (1).
  • If the player picked door 1, the host will open door 0. Switching will earn the player a new car (1).
  • If the player picked door 2, the winning door, the host will open either door 0 or door 1. Either way, if the player switches, he or she will find a goat (0).

It then computes the result as the sum of the previous array, divided by 100.

I'm pretty shaky with J so I'm sure this could be improved further.

\$\endgroup\$
0
3
\$\begingroup\$

R 115 100

The pseudo-simulation answer is 23 characters long:

sum(runif(1e4)>1/3)/100

but here is an actual simulation:

D=1:3
S=function(x)x[sample(length(x),1)]
sum(replicate(1e4,{
C=S(D)
P=S(D)
H=S(D[-c(C,P)])
F=D[-c(P,H)]
C==F
}))/100
  1. D are the possible doors
  2. S is a function to randomly select one item from a vector
  3. C is the the door with the car (random among D)
  4. P is the the door picked by the player (random among D)
  5. H is the door picked by the host (random among D minus C and P)
  6. F is the final door picked by the player (deterministic: D minus P and H)
  7. success is measured by C==F.

returns: [1] 66.731

Edit

I can save a few characters by not assigning to variables and assuming without loss of generality that C==1:

D=1:3;S=function(x)x[sample(length(x),1)];sum(replicate(1e4,{P=S(D);1==D[-c(P,S(D[-c(1,P)]))]}))/100
\$\endgroup\$
2
\$\begingroup\$

Perl, 98 89 83 75 72 71 characters

Here's a serious answer that actually runs the simulation:

sub c{($==rand 2)-"@_"?$=:&c}$n+=$%==c c($%=rand 3)for 1..1e4;say$n/100

In each loop iteration, the player's initial choice is always door #2. First the door with the car is stored in $%, then a different door is selected for Monty Hall to expose. If the remaining door is equal to $%, the round is won.

(Perl puncutation variables $% and $= are used because they do integer truncation for free.)

\$\endgroup\$
2
\$\begingroup\$

Powershell - 168 131 125 115

Golfed code:

nal g Random;1..1e4|%{$C=g 3;$P=g 3;$T+=$C-eq(0..2|?{$_-ne$P-and$_-ne(0..2|?{$_-ne$P-and$_-ne$C}|g)})};"$($T/100)%"

Changes from Original:
Trimmed 53 characters off the original script with some modifications.

  • Removed spaces and parenthesis where PowerShell is forgiving of it.
  • Used a ForEach-Object loop, via the % alias, instead of while.
  • Used number ranges (e.g.: 0..2) instead of explicitly defined arrays.
  • Removed write from the last command - turns out I don't need it after all.
  • Flipped the expression for the host's choice around to use the shorter pipelining syntax.
  • Replaced 10000 with 1e4.
  • Took Joey's suggestion and omitted Get- from Get-Random. (Note: This modification significantly bloats the run time. On my system it jumped from about 20 seconds to nearly a half-hour per run!)
  • Used Rynant's trick of doing $T+=... instead of if(...){$T++}.

Some notes:

This script is intended to be as concise as possible, while also being as thorough a simulation of the Monty Hall scenario as possible. It makes no assumptions as to where the car will be, or which door the player will choose first. Assumptions are not even made for which specific door the host will choose in any given scenario. The only remaining assumptions are those which are actually stated in the Monty Hall problem:

  • The host will choose a door that the player did not pick first, which does not contain the car.
    • If the player picked the door with the car first, that means there are two possible choices for the host.
  • The player's final choice will be neither his initial choice nor the host's choice.

Ungolfed, with comments:

# Setup a single-character alias for Random, to save characters later.
# Note: Script will run a lot (about 500 times) faster if you use Get-Random here.
# Seriously, as it currently is, this script will take about a half-hour or more to run.
# With Get-Random, it'll take less than a minute.
nal g Random;

# Run a Monty Hall simulation for each number from 1 to 10,000 (1e4).
1..1e4|%{

    # Set car location ($C) and player's first pick ($P) to random picks from a pool of 3.
    # Used in this way, Random chooses from 0..2.
    $C=g 3;$P=g 3;

    # Increment win total ($T) if the car is behind the door the player finally chooses.
    # (Player's final choice represented by nested script.)
    $T+=$C-eq(

        # Filter the doors (0..2) to determine player's final choice.
        0..2|?{

            # Player's final choice will be neither their original choice, nor the host's pick.
            # (Host's pick represented by nested script.)
            $_-ne$P-and$_-ne(

                # Filter the doors to determine host's pick.
                0..2|?{

                    # Host picks from door(s) which do not contain the car and were not originally picked by the player.
                    $_-ne$P-and$_-ne$C

                # Send filtered doors to Random for host's pick.
                }|g
            )
        }
    )
};

# After all simulations are complete, output overall win percentage.
"$($T/100)%"

# Variable & alias cleanup. Not included in golfed script.
rv C,P,T;ri alias:g

I've run this script several times and it consistently outputs results very near to two-thirds probability. Some samples:

(As above)

  • 67.02%

(Using Get-Random as the alias definition, instead of just Random)

  • 66.92%
  • 67.71%
  • 66.6%
  • 66.88%
  • 66.68%
  • 66.16%
  • 66.96%
  • 66.7%
  • 65.96%
  • 66.87%
\$\endgroup\$
2
\$\begingroup\$

Ruby 48 40 38

My code doesn't make any assumptions about which door the prize will always be behind or which door the player will always open. Instead, I focused on what causes the player to lose. As per the Wikipedia article:

[...] 2/3 of the time, the initial choice of the player is a door hiding a goat. When that is the case, the host is forced to open the other goat door [...] "Switching" only fails to give the car when the player picks the "right" door (the door hiding the car) to begin with.

So to simulate this (instead of using fixed values), I modeled it as so:

  • the show randomly picks 1 of the 3 doors to hide the prize behind
  • the player then randomly picks 1 of the 3 doors as his first choice
  • the player always switches, so if his first choice was the same as the show's choice, he loses

The code v1:

w=0;10000.times{w+=rand(3)==rand(3)?0:1};p w/1e2

The code v3 (thanks to steenslag and Iszi!):

p (1..1e4).count{rand(3)!=rand(3)}/1e2

Some sample return values:

  • 66.44
  • 66.98
  • 66.33
  • 67.2
  • 65.7
\$\endgroup\$
4
  • 1
    \$\begingroup\$ p (1..10000).count{rand(3)!=rand(3)}/1e2 saves some chars. \$\endgroup\$
    – steenslag
    Dec 6, 2013 at 20:15
  • \$\begingroup\$ @steenslag Ah, indeed it does! Thank you! =) \$\endgroup\$ Dec 6, 2013 at 21:28
  • 1
    \$\begingroup\$ Does Ruby not allow shortcutting powers of 10? E.g: 1e4 for 10000? \$\endgroup\$
    – Iszi
    Dec 6, 2013 at 21:49
  • \$\begingroup\$ @Iszi It does, but scientific notation yields a float, so it can't always be substituted. However, it is a viable substitution in v2, saving 2 more chars! \$\endgroup\$ Dec 6, 2013 at 22:22
1
\$\begingroup\$

Mathematica 42

Count[RandomReal[1,10^4],x_/;x>1/3]/100// N

66.79

\$\endgroup\$
1
\$\begingroup\$

PowerShell, 90

$i=0
1..10000|%{$g=random 3;$n=0..2-ne$g;$g=($n,2)[!!($n-eq2)]|random;$i+=$g-eq2}
$i/10000

Commented:

# Winning door is always #2

$i=0

# Run simulation 1,000 times
1..10000|%{

# Guess a random door
$g=random 3

# Get the doors Not guessed
$n=0..2-ne$g

# Of the doors not guessed, if either is the
# winning door, switch to that door.
# Else, switch to a random door.
$g=($n,2)[!!($n-eq2)]|random

# Increment $i if 
$i+=$g-eq2}

# Result
$i/10000
\$\endgroup\$
1
  • \$\begingroup\$ This does not output a winning percentage in the format specified in the question. Also, save a couple characters by using 1e4 instead of 10000. \$\endgroup\$
    – Iszi
    Nov 20, 2013 at 20:44
1
\$\begingroup\$

C, 101 95

float c,s,t,i;main(){for(;i<1e5;i++,c=rand()%3,s=rand()%3)i>5e4&c!=s?t++:t;printf("%f",t/5e4);}

That's for the actual simulation. For some cheaty rule-bending code, it's only 71 65 59:

p,i;main(){for(;i<1e5;rand()%5>1?i++:p++)printf("%f",p/1e5);}

I didn't do srand() because the rules didn't say I had to. Also, the cheaty version prints out about 30,000 extra numbers because it saves a character. I'm probably missing quite a few tricks, but I did the best I could.

\$\endgroup\$
1
  • \$\begingroup\$ Global variables are guaranteed to be zero on startup. Move your variable declarations out of main and you can drop the =0 initializations. \$\endgroup\$
    – breadbox
    Nov 20, 2013 at 19:58
1
\$\begingroup\$

Python 2: 72 66 64

from random import*
i=10000
exec"i-=randint(0,2)&1;"*i
print.01*i

Example output: 66.49

\$\endgroup\$
5
  • 1
    \$\begingroup\$ You can save a few characters by using exec"i-=randint(0,2)&1;"*i instead of the for loop. \$\endgroup\$ Oct 29, 2013 at 22:09
  • \$\begingroup\$ @WolframH Thanks, I'll update it now. \$\endgroup\$
    – Rees
    Oct 29, 2013 at 23:25
  • \$\begingroup\$ Also, use print.01*i instead of print i/100.. \$\endgroup\$ Oct 31, 2013 at 0:32
  • \$\begingroup\$ Nice solution but you're missing a semicolon. \$\endgroup\$ Nov 13, 2013 at 22:07
  • \$\begingroup\$ Very true. Updating now... \$\endgroup\$
    – Rees
    Dec 6, 2013 at 18:26
0
\$\begingroup\$

Fish - 46 43

This is using the same assumptions that Tristin made:

aa*v>1-:?v$aa*,n;
v*a<$v+1$x! <
>a*0^<  $<

The down direction on x represents you initially picking the correct door, left and right are the cases that you picked a different door, and up is nothing, and will roll again.

Originally, I initialized 10000 with "dd"*, but "dd" had to all be on the same line, and I wasted some whitespace. By snaking aa*a*a* I was able to remove a column, and ultimately 3 characters. There's a little bit of whitespace left that I haven't been able to get rid of, I think this is pretty good though!

\$\endgroup\$
0
\$\begingroup\$

PHP 140

But i think that this is not working right. Any tip? I'm getting values from 49 to 50.

$v=0;//victorys
for($i=0;$i<1e4;$i++){    
    //while the selection of the host ($r) equals the player selection or the car
    //h=removed by host, p=player, c=car
    while(in_array($h=rand(1,3),[$p=rand(1,3),$c=rand(1,3)])){}
    ($p!=$c)?$v+=1:0; //if the player changes the selection    
}
echo ($v/1e4)*100;
\$\endgroup\$
2
  • \$\begingroup\$ "If the player changes the selection"? \$\endgroup\$
    – Timtech
    Nov 28, 2013 at 16:33
  • \$\begingroup\$ Sorry my English is not good. I mean, first i do a While trying to get aceptable values. Because the "host" can't remove a door containing the car OR the door that you choose. Then i have $p (players choice) and $c (where the car is). The OP said that you must take the percentage of winning when you switch, so i only count the result as a "victory" when $p!=$c (you switch your choice to the other door and you win). \$\endgroup\$ Nov 28, 2013 at 16:59
0
\$\begingroup\$

Game Maker Language, 19 (51 w/ loop)

show_message(200/3)

It outputs 66.67! This is the correct probability ;)


The serious-mode code, 51 characters:

repeat(10000)p+=(random(1)<2/3);show_message(p/100)

Make sure to compile with treat all uninitialized variables as 0.


The oldest code, 59 characters:

for(i=0;i<10000;i+=1)p+=(random(1)<2/3);show_message(p/100)

Again, make sure to compile with treat all uninitialized variables as 0.

The output was 66.23

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.