Skip to content

Instantly share code, notes, and snippets.

@shreve
Last active December 23, 2017 02:17
Show Gist options
  • Select an option

  • Save shreve/4a9e161a058c321538e00855d0dae126 to your computer and use it in GitHub Desktop.

Select an option

Save shreve/4a9e161a058c321538e00855d0dae126 to your computer and use it in GitHub Desktop.
The Monty Hall Problem
# Simulation of the Monty Hall problem
#
# In a given game, there are three doors with a prize behind one.
# The player picks one of the doors.
# Monty removes one of the non-selected doors that isn't the prize door.
# There are now two doors left in play, and one contains a prize.
# Monty asks the player if they'd like to switch doors.
#
# What should the player do? What is the probability the player will win if
# they switch doors vs staying the same?
#
# The program randomly simulates the results of many rounds of the game to see
# what the probabilities are like.
#
# Game
#
# This class is responsible for selecting the doors and detecting if the game
# was won by the player.
#
class Game
def initialize
won?
end
# Compare the prize door to either the new door or original door depending on
# whether the player switched or not.
def won?
if switched?
new_door == prize_door
else
original_door == prize_door
end
end
def switched?
defined?(@sw) ? @sw : @sw = [true, false].sample
end
def original_door
@od ||= random_door
end
def prize_door
@pd ||= random_door
end
def new_door
@nd ||= begin
# The new door cannot be the original door
doors = [0, 1, 2] - [original_door]
# Remove one of the doors that isn't the original or prize door.
doors -= ([0, 1, 2] - [original_door, prize_door])
# Pick one of the remaining doors
doors.sample
end
end
def random_door
rand(3)
end
def inspect
"[ #{door_ind(0)} ][ #{door_ind(1)} ][ #{door_ind(2)} ] #{'Won!' if won?}"
end
def to_s
inspect
end
private
def door_ind(index)
i = ''
i << 'O' if index == original_door
i << 'N' if siwtched? && index == new_door
i << 'P' if index == prize_door
i
end
end
def print_report(set)
won = set.select(&:won?).count
puts "total: #{set.count}"
puts "won: #{won} (#{(won.to_f / set.count * 100).round(1)}%)"
puts ''
end
games = Array.new(10_000).map { Game.new }
switched = games.select(&:switched?)
stayed = games - switched
puts 'Switched'
puts print_report(switched)
puts 'Stayed'
puts print_report(stayed)
# The expected output reads something like the following
#
# Switched
# total: 5065
# won: 3372 (66.6%)
#
# Stayed
# total: 4935
# won: 1656 (33.6%)
#
# This may be a surprising result, that switching your door more likely leads
# to a win, however this is normal probabilities.
#
# An intuitive explanation is that switching wins if you picked a bad door.
# It's more likely that you picked a bad door than the right one, so it's more
# likely that switching will win.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment