Numpy and how to cheat on game shows 

The Monty Hall problem is a statistical puzzle based on an old US TV show called Let's Make a Deal. The puzzle goes as follows. On the game show contestants have to guess where the star prize is hidden. They are given a choice of 3 doors. Behind 2 doors are goats and behind 1 door is the prize, say, a car. After the contestant makes their choice the presenter opens a door. This is always a door the contestant did not pick and is always a goat door. Now the contest is asked if they want to stick with their original guess or switch to the other unopened door. Is there any advantage in switching doors? This is they key question. The answer is counter-intuitive, and many high level mathematicians struggled to accept it when the puzzle was originally presented in 1990. However using the NumPy library we can write a simulation that will provide strong proof for what will be a surprising answer to the the Monty Hall problem.

 

import numpy as np

 

def simulate_prizedoor(nsim):
    return np.random.randint(3, size=nsim)
    
print simulate_prizedoor(3)

>>> [1 2 0]

First we simulated hiding a prize behind one of three doors. So in the above array the prize was behind door 1 on the first game, 2 on the second and 0 on the third. 

 

def simulate_guess(nsim):
    return np.random.randint(3, size=nsim)

print simulate_guess(5)

>>> [1 0 2 0 0]

Next we write a function to simulate players guessing which door the prize is behind. In the first game the player guesses door 1, in the second game door 0, etc.


def gd(prizedoors, guesses):
    monty = np.empty(prizedoors.size, dtype=int)
    for n in range(0,prizedoors.size):
        #the first loop iterates over each element in the prizedoors & guesses arrays
        for x in range(0,3):
            #the second loop tries to find a number between 0 and 2 that is not in the first element either the prizedoors & guesses arrays
            if (x != prizedoors[n]) & (x != guesses[n]):
                monty[n] = x
                break
                #if a number in the second loop is found it is inserted into the monty array
    return monty

 

prize = simulate_prizedoor(3)
guess = simulate_guess(3)
print prize
print guess
print gd(prize, guess)

>>> [2 0 2]
>>> [1 1 1]
>>> [0 2 0]

The function gd() simulates randomly revealing one of the goat doors that the contestant did not pick. So the third array, generated by the gd() function, will always be different to the prize array, and also the guess array, if the guess was for the other goat door.


def sg(guesses, goatdoors):
    switch = np.empty(guesses.size, dtype=int)
    for n in range(0,guesses.size):
        #the first loop iterates over each element in the prizedoors & guesses arrays
        for x in range(0,3):
            #the second loop tries to find a number between 0 and 2 that is not in the first element of the prizedoors & guesses arrays
            if (x != guesses[n]) & (x != goatdoors[n]):
                switch[n] = x
                break
                #if a number in the second loop is found it is inserted into the monty array
    return switch

 

prize = simulate_prizedoor(3)
guess = simulate_guess(3)
goat = gd(prize, guess)
print guess
print goat
print sg(guess, goat)

>>> [1 2 2]
>>> [2 0 1]
>>> [0 1 0]

The sg() function represents switching guess after the host opens a goat door. As we can see this function will always produce a different result to both the initial guess in the first array and the revealed goat door in the second array.

 

def win_percentage(guesses, prizedoors):
    win = 0
    loss = 0
    for x in range(0, guesses.size):
        if guesses[x] == prizedoors[x]:
            win += 1
        else:
            loss += 1
    return (win/(guesses.size*1.0))*100

 

guesses = simulate_guess(10000)
prizedoors = simulate_prizedoor(10000)
print win_percentage(guesses, prizedoors)

>>> 32.38

Now we can start to calculate the results of the puzzle. The function win_percentage() shows us the the percentage of times the original guess matches the prize door.

 

def final(prizedoors, guesses):
    new_gd = gd(prizedoors, guesses)
    switch = sg(guesses, new_gd)
    print win_percentage(prizedoors, guesses)
    print win_percentage(prizedoors, switch)

 

prizedoors = simulate_prizedoor(10000)
guesses = simulate_guess(10000)
print final(prizedoors, guesses)

>>> 33.47
>>> 66.53
>>> None

The last function, final(), gives us both the result of sticking with the original guess, and switching guesses once the goat door is revealed. As we can see when we simulate the Monty Hall problem 10000 times the likelihood of sticking with our original guess and winning the game is 33%. The likelihood of winning when we switch guesses is twice as high at 67%. This may seem surprising. But thanks to the power of Python and NumPy we have solid evidence to support this counter-intuitive conclusion.