É este um bom ou mau 'simulação' para Monty Hall? Por quê?
-
12-09-2019 - |
Pergunta
Através tentando explicar a problema de Monty Salão a um amigo durante a aula de ontem, acabamos codificá-lo em Python para provar que se você sempre trocar, você vai ganhar 2/3 vezes. Nós viemos com esta:
import random as r
#iterations = int(raw_input("How many iterations? >> "))
iterations = 100000
doors = ["goat", "goat", "car"]
wins = 0.0
losses = 0.0
for i in range(iterations):
n = r.randrange(0,3)
choice = doors[n]
if n == 0:
#print "You chose door 1."
#print "Monty opens door 2. There is a goat behind this door."
#print "You swapped to door 3."
wins += 1
#print "You won a " + doors[2] + "\n"
elif n == 1:
#print "You chose door 2."
#print "Monty opens door 1. There is a goat behind this door."
#print "You swapped to door 3."
wins += 1
#print "You won a " + doors[2] + "\n"
elif n == 2:
#print "You chose door 3."
#print "Monty opens door 2. There is a goat behind this door."
#print "You swapped to door 1."
losses += 1
#print "You won a " + doors[0] + "\n"
else:
print "You screwed up"
percentage = (wins/iterations) * 100
print "Wins: " + str(wins)
print "Losses: " + str(losses)
print "You won " + str(percentage) + "% of the time"
O meu amigo pensei que esta era uma boa maneira de fazer isso (e é uma boa simulação para ele), mas eu tenho minhas dúvidas e preocupações. É o suficiente realmente aleatório?
O problema que tenho com ele é que as todas as escolhas são meio difícil codificado em.
É este um bom ou mau 'simulação' para o problema de Monty Hall? Como assim?
É possível que você venha com uma versão melhor?
Solução
A sua solução é bom, mas se você quiser uma simulação mais rigorosa do problema tal como colocada (e um pouco maior qualidade Python ;-), tente:
import random
iterations = 100000
doors = ["goat"] * 2 + ["car"]
change_wins = 0
change_loses = 0
for i in xrange(iterations):
random.shuffle(doors)
# you pick door n:
n = random.randrange(3)
# monty picks door k, k!=n and doors[k]!="car"
sequence = range(3)
random.shuffle(sequence)
for k in sequence:
if k == n or doors[k] == "car":
continue
# now if you change, you lose iff doors[n]=="car"
if doors[n] == "car":
change_loses += 1
else:
change_wins += 1
print "Changing has %s wins and %s losses" % (change_wins, change_loses)
perc = (100.0 * change_wins) / (change_wins + change_loses)
print "IOW, by changing you win %.1f%% of the time" % perc
a saída típica é:
Changing has 66721 wins and 33279 losses
IOW, by changing you win 66.7% of the time
Outras dicas
Você mencionou que todas as escolhas são codificados. Mas se você olhar mais de perto, você vai perceber que o que você acha que são 'escolhas' são, na verdade, não escolhas em tudo. A decisão de Monty é sem perda de generalidade, uma vez que ele sempre escolhe a porta com a cabra por trás dele. Sua troca é sempre determinado pelo que Monty escolhe, e uma vez que a "escolha" do Monty realmente não foi uma escolha, nem é sua. Sua simulação dá os resultados corretos ..
Eu gosto de algo como isto.
#!/usr/bin/python
import random
CAR = 1
GOAT = 0
def one_trial( doors, switch=False ):
"""One trial of the Monty Hall contest."""
random.shuffle( doors )
first_choice = doors.pop( )
if switch==False:
return first_choice
elif doors.__contains__(CAR):
return CAR
else:
return GOAT
def n_trials( switch=False, n=10 ):
"""Play the game N times and return some stats."""
wins = 0
for n in xrange(n):
doors = [CAR, GOAT, GOAT]
wins += one_trial( doors, switch=switch )
print "won:", wins, "lost:", (n-wins), "avg:", (float(wins)/float(n))
if __name__=="__main__":
import sys
n_trials( switch=eval(sys.argv[1]), n=int(sys.argv[2]) )
$ ./montyhall.py True 10000
won: 6744 lost: 3255 avg: 0.674467446745
Eu não tinha ouvido falar do problema de Monty Salão antes me deparei com esta questão. Eu pensei que era interessante, então eu li sobre isso e criou um c # simulação. É uma espécie de pateta, uma vez que simula o jogo-show e não apenas o problema.
publiquei a fonte e liberação no CodePlex:
Aqui está a minha versão ...
import random
wins = 0
for n in range(1000):
doors = [1, 2, 3]
carDoor = random.choice(doors)
playerDoor = random.choice(doors)
hostDoor = random.choice(list(set(doors) - set([carDoor, playerDoor])))
# To stick, just comment out the next line.
(playerDoor, ) = set(doors) - set([playerDoor, hostDoor]) # Player swaps doors.
if playerDoor == carDoor:
wins += 1
print str(round(wins / float(n) * 100, 2)) + '%'
Aqui está uma versão interativa:
from random import shuffle, choice
cars,goats,iters= 0, 0, 100
for i in range(iters):
doors = ['goat A', 'goat B', 'car']
shuffle(doors)
moderator_door = 'car'
#Turn 1:
selected_door = choice(doors)
print selected_door
doors.remove(selected_door)
print 'You have selected a door with an unknown object'
#Turn 2:
while moderator_door == 'car':
moderator_door = choice(doors)
doors.remove(moderator_door)
print 'Moderator has opened a door with ', moderator_door
#Turn 3:
decision=raw_input('Wanna change your door? [yn]')
if decision=='y':
prise = doors[0]
print 'You have a door with ', prise
elif decision=='n':
prise = selected_door
print 'You have a door with ', prise
else:
prise = 'ERROR'
iters += 1
print 'ERROR:unknown command'
if prise == 'car':
cars += 1
elif prise != 'ERROR':
goats += 1
print '==============================='
print ' RESULTS '
print '==============================='
print 'Goats:', goats
print 'Cars :', cars
A minha solução com compreensão de lista para simular o problema
from random import randint
N = 1000
def simulate(N):
car_gate=[randint(1,3) for x in range(N)]
gate_sel=[randint(1,3) for x in range(N)]
score = sum([True if car_gate[i] == gate_sel[i] or ([posible_gate for posible_gate in [1,2,3] if posible_gate != gate_sel[i]][randint(0,1)] == car_gate[i]) else False for i in range(N)])
return 'you win %s of the time when you change your selection.' % (float(score) / float(N))
Simular impressão (N)
Não é meu sample
# -*- coding: utf-8 -*-
#!/usr/bin/python -Ou
# Written by kocmuk.ru, 2008
import random
num = 10000 # number of games to play
win = 0 # init win count if donot change our first choice
for i in range(1, num): # play "num" games
if random.randint(1,3) == random.randint(1,3): # if win at first choice
win +=1 # increasing win count
print "I donot change first choice and win:", win, " games"
print "I change initial choice and win:", num-win, " games" # looses of "not_change_first_choice are wins if changing
Eu encontrei este para ser a forma mais intuitiva de resolver o problema.
import random
# game_show will return True/False if the participant wins/loses the car:
def game_show(knows_bayes):
doors = [i for i in range(3)]
# Let the car be behind this door
car = random.choice(doors)
# The participant chooses this door..
choice = random.choice(doors)
# ..so the host opens another (random) door with no car behind it
open_door = random.choice([i for i in doors if i not in [car, choice]])
# If the participant knows_bayes she will switch doors now
if knows_bayes:
choice = [i for i in doors if i not in [choice, open_door]][0]
# Did the participant win a car?
if choice == car:
return True
else:
return False
# Let us run the game_show() for two participants. One knows_bayes and the other does not.
wins = [0, 0]
runs = 100000
for x in range(0, runs):
if game_show(True):
wins[0] += 1
if game_show(False):
wins[1] += 1
print "If the participant knows_bayes she wins %d %% of the time." % (float(wins[0])/runs*100)
print "If the participant does NOT knows_bayes she wins %d %% of the time." % (float(wins[1])/runs*100)
Este algo saídas como
If the participant knows_bayes she wins 66 % of the time.
If the participant does NOT knows_bayes she wins 33 % of the time.
Leia um capítulo sobre o famoso problema de Monty Salão hoje. Esta é a minha solução.
import random
def one_round():
doors = [1,1,0] # 1==goat, 0=car
random.shuffle(doors) # shuffle doors
choice = random.randint(0,2)
return doors[choice]
#If a goat is chosen, it means the player loses if he/she does not change.
#This method returns if the player wins or loses if he/she changes. win = 1, lose = 0
def hall():
change_wins = 0
N = 10000
for index in range(0,N):
change_wins += one_round()
print change_wins
hall()
No entanto, outra "prova", desta vez com Python 3. Observe o uso de geradores para selecionar 1) que porta Monty abre, e 2), que porta o jogador muda para.
import random
items = ['goat', 'goat', 'car']
num_trials = 100000
num_wins = 0
for trial in range(num_trials):
random.shuffle(items)
player = random.randrange(3)
monty = next(i for i, v in enumerate(items) if i != player and v != 'car')
player = next(x for x in range(3) if x not in (player, monty))
if items[player] == 'car':
num_wins += 1
print('{}/{} = {}'.format(num_wins, num_trials, num_wins / num_trials))
Monty não abre a porta com o carro - que é o ponto de todo o show (ele não é seu amigo um tem conhecimento do que está por trás de cada porta)
Outro exemplo de código está disponível em: http://standardwisdom.com/ / code-amostras softwarejournal / Monty-hall-python /
O código é um pouco mais e não pode usar alguns dos recursos interessantes do Python, mas eu espero que seja bem legível. Usado Python precisamente porque eu não tenho nenhuma experiência nela, por isso o feedback é apreciado.
Aqui é diferente variante eu acho mais intuitiva. Espero que isso ajude!
import random
class MontyHall():
"""A Monty Hall game simulator."""
def __init__(self):
self.doors = ['Door #1', 'Door #2', 'Door #3']
self.prize_door = random.choice(self.doors)
self.contestant_choice = ""
self.monty_show = ""
self.contestant_switch = ""
self.contestant_final_choice = ""
self.outcome = ""
def Contestant_Chooses(self):
self.contestant_choice = random.choice(self.doors)
def Monty_Shows(self):
monty_choices = [door for door in self.doors if door not in [self.contestant_choice, self.prize_door]]
self.monty_show = random.choice(monty_choices)
def Contestant_Revises(self):
self.contestant_switch = random.choice([True, False])
if self.contestant_switch == True:
self.contestant_final_choice = [door for door in self.doors if door not in [self.contestant_choice, self.monty_show]][0]
else:
self.contestant_final_choice = self.contestant_choice
def Score(self):
if self.contestant_final_choice == self.prize_door:
self.outcome = "Win"
else:
self.outcome = "Lose"
def _ShowState(self):
print "-" * 50
print "Doors %s" % self.doors
print "Prize Door %s" % self.prize_door
print "Contestant Choice %s" % self.contestant_choice
print "Monty Show %s" % self.monty_show
print "Contestant Switch %s" % self.contestant_switch
print "Contestant Final Choice %s" % self.contestant_final_choice
print "Outcome %s" % self.outcome
print "-" * 50
Switch_Wins = 0
NoSwitch_Wins = 0
Switch_Lose = 0
NoSwitch_Lose = 0
for x in range(100000):
game = MontyHall()
game.Contestant_Chooses()
game.Monty_Shows()
game.Contestant_Revises()
game.Score()
# Tally Up the Scores
if game.contestant_switch and game.outcome == "Win": Switch_Wins = Switch_Wins + 1
if not(game.contestant_switch) and game.outcome == "Win": NoSwitch_Wins = NoSwitch_Wins + 1
if game.contestant_switch and game.outcome == "Lose": Switch_Lose = Switch_Lose + 1
if not(game.contestant_switch) and game.outcome == "Lose": NoSwitch_Lose = NoSwitch_Lose + 1
print Switch_Wins * 1.0 / (Switch_Wins + Switch_Lose)
print NoSwitch_Wins * 1.0 / (NoSwitch_Wins + NoSwitch_Lose)
O aprendizado ainda é o mesmo, que a mudança aumenta suas chances de ganhar, 0,665025416127 vs 0,33554730611 do prazo acima.
Aqui está um que fiz anteriormente:
import random
def game():
"""
Set up three doors, one randomly with a car behind and two with
goats behind. Choose a door randomly, then the presenter takes away
one of the goats. Return the outcome based on whether you stuck with
your original choice or switched to the other remaining closed door.
"""
# Neither stick or switch has won yet, so set them both to False
stick = switch = False
# Set all of the doors to goats (zeroes)
doors = [ 0, 0, 0 ]
# Randomly change one of the goats for a car (one)
doors[random.randint(0, 2)] = 1
# Randomly choose one of the doors out of the three
choice = doors[random.randint(0, 2)]
# If our choice was a car (a one)
if choice == 1:
# Then stick wins
stick = True
else:
# Otherwise, because the presenter would take away the other
# goat, switching would always win.
switch = True
return (stick, switch)
Eu também tive código para executar o jogo muitas vezes, e armazenados isso e a saída de amostra neste repostory .
Aqui está a minha solução para o problema MontyHall implementado em python.
Esta solução faz uso de numpy para a velocidade, ele também permite que você altere o número de portas.
def montyhall(Trials:"Number of trials",Doors:"Amount of doors",P:"Output debug"):
N = Trials # the amount of trial
DoorSize = Doors+1
Answer = (nprand.randint(1,DoorSize,N))
OtherDoor = (nprand.randint(1,DoorSize,N))
UserDoorChoice = (nprand.randint(1,DoorSize,N))
# this will generate a second door that is not the user's selected door
C = np.where( (UserDoorChoice==OtherDoor)>0 )[0]
while (len(C)>0):
OtherDoor[C] = nprand.randint(1,DoorSize,len(C))
C = np.where( (UserDoorChoice==OtherDoor)>0 )[0]
# place the car as the other choice for when the user got it wrong
D = np.where( (UserDoorChoice!=Answer)>0 )[0]
OtherDoor[D] = Answer[D]
'''
IfUserStays = 0
IfUserChanges = 0
for n in range(0,N):
IfUserStays += 1 if Answer[n]==UserDoorChoice[n] else 0
IfUserChanges += 1 if Answer[n]==OtherDoor[n] else 0
'''
IfUserStays = float(len( np.where((Answer==UserDoorChoice)>0)[0] ))
IfUserChanges = float(len( np.where((Answer==OtherDoor)>0)[0] ))
if P:
print("Answer ="+str(Answer))
print("Other ="+str(OtherDoor))
print("UserDoorChoice="+str(UserDoorChoice))
print("OtherDoor ="+str(OtherDoor))
print("results")
print("UserDoorChoice="+str(UserDoorChoice==Answer)+" n="+str(IfUserStays)+" r="+str(IfUserStays/N))
print("OtherDoor ="+str(OtherDoor==Answer)+" n="+str(IfUserChanges)+" r="+str(IfUserChanges/N))
return IfUserStays/N, IfUserChanges/N
Eu só descobri que a relação mundial de ganhar é de 50% ea relação de perder é 50% ... É como a proporção em ganhar ou perder com base na opção final selecionado.
- % Wins (hospedados): 16,692
- % Wins (switching): 33,525
- % Perdas (hospedados): 33,249
- % Perdas (switching): 16,534
Aqui está o meu código, que difere da sua + com comentários comentadas para que você possa executá-lo com pequenas iterações:
import random as r
#iterations = int(raw_input("How many iterations? >> "))
iterations = 100000
doors = ["goat", "goat", "car"]
wins_staying = 0
wins_switching = 0
losses_staying = 0
losses_switching = 0
for i in range(iterations):
# Shuffle the options
r.shuffle(doors)
# print("Doors configuration: ", doors)
# Host will always know where the car is
car_option = doors.index("car")
# print("car is in Option: ", car_option)
# We set the options for the user
available_options = [0, 1 , 2]
# The user selects an option
user_option = r.choice(available_options)
# print("User option is: ", user_option)
# We remove an option
if(user_option != car_option ) :
# In the case the door is a goat door on the user
# we just leave the car door and the user door
available_options = [user_option, car_option]
else:
# In the case the door is the car door
# we try to get one random door to keep
available_options.remove(available_options[car_option])
goat_option = r.choice(available_options)
available_options = [goat_option, car_option]
new_user_option = r.choice(available_options)
# print("User final decision is: ", new_user_option)
if new_user_option == car_option :
if(new_user_option == user_option) :
wins_staying += 1
else :
wins_switching += 1
else :
if(new_user_option == user_option) :
losses_staying += 1
else :
losses_switching += 1
print("%Wins (staying): " + str(wins_staying / iterations * 100))
print("%Wins (switching): " + str(wins_switching / iterations * 100))
print("%Losses (staying) : " + str(losses_staying / iterations * 100))
print("%Losses (switching) : " + str(losses_switching / iterations * 100))