Simulate the Monty Hall Problem

8

1

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%

bendytree

Posted 2013-09-30T20:34:45.767

Reputation: 211

Question was closed 2016-05-16T14:55:20.143

I had trouble with the Monty Hall problem. The point is indeed that Monty Hall only selects doors which do not hide a car. I got the reasoning once I made the same reasoning with 100 doors, 1 car. You choose a door. 1% of the cases you’re right, 99% wrong. If you’re wrong (99%), then Monty opens the 98 doors, the 99 always hide the car; switching is always right. If you’re right (1%), Monty opens 98 doors at random. Not switching is always right. So, in 99% of the cases, switching is right; in 1% not switching is right. You should probably switch. – Édouard – 2016-05-04T19:06:31.853

5The host knows where the car is, so he never opens a door with the car behind it – bendytree – 2013-09-30T21:19:30.820

6As 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? – breadbox – 2013-10-01T00:32:51.547

An interesting historical perspective (on the problem, not the golfing): Which Door Has the Cadillac?.

– Cary Swoveland – 2013-10-01T22:29:10.647

3The 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. – Iszi – 2013-10-25T19:09:15.263

I took first trying 50 cycles each way in real life and then getting him to code such a simulation himself to convince a friend that switching is the right thing to do. – dmckee --- ex-moderator kitten – 2013-10-30T02:27:35.560

@dmckee I started coding a simulation of it way back in grade 11 when I learned Visual Basic. I was trying to convince my dad about this problem, and as programming it I started optimizing pieces of code you REALLY start to realise that this is in fact true. Coding this yourself is the best way to convince yourself of this problem. – Cruncher – 2013-11-20T20:17:31.353

Also, you can never get a result of 66.66733% with exactly 10,000 simulations. :P – Joe Z. – 2013-11-20T20:26:37.627

1Enumerating all the possibilities is at least as illuminating as generating them randomly... – Daniel Cristofani – 2013-11-20T22:03:51.577

Answers

4

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.

tristin

Posted 2013-09-30T20:34:45.767

Reputation: 189

1Looks like CoffeeScript allows us to shave off a single character: i=s=0;s+=Math.random()<2/3while i++<1e4;alert s/100 – Kerrick – 2013-10-05T04:55:13.787

3

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.

p.s.w.g

Posted 2013-09-30T20:34:45.767

Reputation: 573

3

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

flodel

Posted 2013-09-30T20:34:45.767

Reputation: 2 345

2

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.)

breadbox

Posted 2013-09-30T20:34:45.767

Reputation: 6 893

2

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%

Iszi

Posted 2013-09-30T20:34:45.767

Reputation: 2 369

2

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

Jonathan Hefner

Posted 2013-09-30T20:34:45.767

Reputation: 61

1p (1..10000).count{rand(3)!=rand(3)}/1e2 saves some chars. – steenslag – 2013-12-06T20:15:50.217

@steenslag Ah, indeed it does! Thank you! =) – Jonathan Hefner – 2013-12-06T21:28:58.947

1Does Ruby not allow shortcutting powers of 10? E.g: 1e4 for 10000? – Iszi – 2013-12-06T21:49:34.507

@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! – Jonathan Hefner – 2013-12-06T22:22:56.557

1

Mathematica 42

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

66.79

DavidC

Posted 2013-09-30T20:34:45.767

Reputation: 24 524

1

Python 2: 72 66 64

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

Example output: 66.49

Rees

Posted 2013-09-30T20:34:45.767

Reputation: 111

1You can save a few characters by using exec"i-=randint(0,2)&1;"*i instead of the for loop. – Reinstate Monica – 2013-10-29T22:09:56.570

@WolframH Thanks, I'll update it now. – Rees – 2013-10-29T23:25:33.267

Also, use print.01*i instead of print i/100.. – Reinstate Monica – 2013-10-31T00:32:16.707

Nice solution but you're missing a semicolon. – Daniel Lubarov – 2013-11-13T22:07:48.517

Very true. Updating now... – Rees – 2013-12-06T18:26:19.197

1

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

Rynant

Posted 2013-09-30T20:34:45.767

Reputation: 2 353

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. – Iszi – 2013-11-20T20:44:34.497

1

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.

Stuntddude

Posted 2013-09-30T20:34:45.767

Reputation: 586

Global variables are guaranteed to be zero on startup. Move your variable declarations out of main and you can drop the =0 initializations. – breadbox – 2013-11-20T19:58:40.957

0

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!

Cruncher

Posted 2013-09-30T20:34:45.767

Reputation: 2 135

0

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;

Carlos Goce

Posted 2013-09-30T20:34:45.767

Reputation: 141

"If the player changes the selection"? – Timtech – 2013-11-28T16:33:18.963

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). – Carlos Goce – 2013-11-28T16:59:59.753

0

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

Timtech

Posted 2013-09-30T20:34:45.767

Reputation: 12 038