shibli049
shibli049's Blog

Follow

shibli049's Blog

Follow
A naive simulation of the Monty Hall problem

A naive simulation of the Monty Hall problem

shibli049's photo
shibli049
·May 14, 2022·

3 min read

Play this article

Monty Hall problem is a probability puzzle, named after the original host of "Let's Make a Deal".

The problem became famous as a question from a reader's letter in the "Ask Marilyn" column in Parade magazine in 1990.

Problem statement:

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?"

Now, is it to your advantage to switch your choice?

Solution

You should switch the door. Because, the switching strategy has a 66.67% chance of winning, while the strategy that remains firm with the initial choice has only a 33.33% chance of winning.

Many readers of Parade magazine denied the explanation that switching is beneficial. Approximately ten thousand Parade magazine readers, including many readers with PhDs wrote to the author that the explanation was wrong. Even with explanations, simulations, and formal proofs, many still don't accept the answer of switching as the strategy.

Paul Erdős, one of the most prolific Hungarian mathematicians in history, remained unconvinced until he was shown a computer simulation demonstrating Marilyn vos Savant's predicted result.

I was also confused with the explanation, so I tried to write a naive python simulation of this problem.

Simulation

# ---------------- the game ----------------
# player choose a door, and switch based on flag
def play_the_game(switch_door = False):
    # number of doors
    door_num = 3
    doors, choosen = put_the_prize_behind_door(door_num)
    # player choose a random door
    player_choice = rand_range(door_num)
    if switch_door:
        # host show a door, behind which there is a goat
        shown = show_door(door_num, choosen, player_choice)
        final_choice = switch_to_new_door(doors, player_choice, shown)
        return len(final_choice) == 1 and final_choice[0] == 1
    else:
        # no matter which door the presenter shows, player's decision is final
        return doors[player_choice] == 1

Now simulate the game N number of times, the bigger the N, the better the results.

# ---------------- tests ----------------
def simulate(N = 10001, switch_door = False):
    success_count = 0
    for i in range(N):
        if play_the_game(switch_door):
            success_count += 1
    return generate_stat(switch_door, success_count, N)

if __name__ == "__main__":
    n = 9999_99 # the bigger the size, the better the result

    switch_rates = simulate(n, True)
    print(switch_rates)

    other_rates = simulate(n, False)
    print(other_rates)

Result

With this simulation, I got a 66.6% win when switching the door. And, 33.3% when not switching the door.

The solution is so counterintuitive it seems absurd. Nevertheless, the solution is true in my simulation, and in many other simulations.

Appendix

# ---------------- helpers ----------------
# creates a random number between 0 and n
def rand_range(n):
    return random.randrange(n)

# choose an item randomly from the list
def rand_item(lst):
    return random.choice(lst)

def put_the_prize_behind_door(door_num):
    # initialize the doors
    doors = [0] * door_num
    # choose a random door to put the prize behind the door
    choosen = rand_range(door_num)
    # prize is set
    doors[choosen] = 1
    return doors,choosen


def show_door(door_num, choosen, player_choice):
    # remove the door player choose, and the door behind which the prize is
    choice = [ele for ele in range(door_num) if ele not in [choosen, player_choice]]
    return rand_item(choice)

def switch_to_new_door(doors, player_choice, shown):
    # remove the shown door and player choice and choose the 3rd door
    return [doors[ind] for ind, elem in enumerate(doors) if ind not in [shown, player_choice]]

def generate_stat(switch_door, success_count, total):
    return f"Switch door: {switch_door}, Total: {total}, Success: {success_count}, percent: {((1.0 * success_count)/total) * 100}"

Did you find this article valuable?

Support shibli049 by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this