Skip to content

Instantly share code, notes, and snippets.

@Michael-J-Ward
Last active September 22, 2019 18:38
Show Gist options
  • Save Michael-J-Ward/e7e939213ea8448124ac85220163cd54 to your computer and use it in GitHub Desktop.
Save Michael-J-Ward/e7e939213ea8448124ac85220163cd54 to your computer and use it in GitHub Desktop.
Monte Carlo'ing a modified monty python variant
"""Monte Carlo'ing Hillogram's Monty Hall Variant
Hillelogram on twitter came up with a surprising Monty Hall Variant: https://twitter.com/hillelogram/status/1175125663247998978
[BEGIN EDIT]
@hillelogram gives credit to this paper https://pubs.aeaweb.org/doi/pdfplus/10.1257/jep.33.3.144
/u/edderiofer on reddit pointed out that this is in fact the "Monty Crawl" variant from here: http://www.probability.ca/jeff/writing/montyfall.pdf
[END EDIT]
This source code is modified from its original version here: https://blog.teamtreehouse.com/modeling-monty-hall-problem-python
"""
from collections import namedtuple
import random
# Create an easy-to-return and easy-to-inspect data
# structure for Game results.
Results = namedtuple('Results', ['won', 'initial_door', 'monty_door'])
class Door:
def __init__(self, location):
self.location = location
self.prize = False
self.opened = False
class Game:
"""Monty Hall, always switch"""
def __init__(self, monty_selection_func):
self.monty_selection_func = monty_selection_func
self.chosen_door = None
self.initial_door = None
self.monty_door = None
self.doors = [
Door(i) for i in range(1, 4)
]
# Pick a random door and give it a prize
random.choice(self.doors).prize = True
def pick_door(self):
self.initial_door = random.choice(self.doors)
def open_false_door(self):
# Get the non-chosen doors
available_doors = [door for door in self.doors
if door is not self.initial_door
and not door.prize]
self.monty_door = self.monty_selection_func(available_doors)
self.monty_door.opened = True
def switch_doors(self):
# Always Switch
available_door = [door for door in self.doors
if door is not self.initial_door
and not door.opened][0]
self.chosen_door = available_door
@classmethod
def play(cls, monty_selection_func):
game = cls(monty_selection_func)
game.pick_door()
game.open_false_door()
game.switch_doors()
won = game.chosen_door.prize
initial_door = game.initial_door.location
monty_door = game.monty_door.location
return Results(won, initial_door, monty_door)
def monty_chooses_lowest(doors):
return doors[0]
def monty_chooses_random(doors):
return random.choice(doors)
# Play play 1e6 runs of the game to get significant numbers
num_simulations = int(1e6)
games = [Game.play(monty_chooses_lowest) for _ in range(num_simulations)]
# Find Games where we select 2 and monty selects 1
scenario_games = [game for game in games if game.initial_door == 2 and game.monty_door == 1]
wins = sum(x.won for x in scenario_games)
num_scenario_games = len(scenario_games)
win_percentage = 100 * wins / num_scenario_games
print("Won {:.2f}% when monty chose the lowest non-prize door: {:,} out of {:,}".format(win_percentage, wins, num_scenario_games))
# Play play 1e6 runs of the game to get significant numbers
num_simulations = int(1e6)
games = [Game.play(monty_chooses_random) for _ in range(num_simulations)]
# Find Games where we select 2 and monty selects 1
scenario_games = [game for game in games if game.initial_door == 2 and game.monty_door == 1]
wins = sum(x.won for x in scenario_games)
num_scenario_games = len(scenario_games)
win_percentage = 100 * wins / num_scenario_games
print("Won {:.2f}% when monty chooses random non-prize door: {:,} out of {:,}".format(win_percentage, wins, num_scenario_games))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment