【问题标题】:Is this a good or bad 'simulation' for Monty Hall? How come? [closed]这对蒙蒂霍尔来说是好还是坏的“模拟”?怎么会? [关闭]
【发布时间】:2010-11-17 21:15:09
【问题描述】:

通过昨天在课堂上尝试向朋友解释Monty Hall problem,我们最终用 Python 对其进行了编码,以证明如果你总是交换,你将赢得 2/3 次。我们想出了这个:

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"

我的朋友认为这是一个很好的方法(并且是一个很好的模拟),但我有我的怀疑和担忧。它真的足够随机吗?

我遇到的问题是所有选择都是硬编码的。

对于蒙蒂霍尔问题,这是一个好还是坏的“模拟”?怎么会?

你能想出一个更好的版本吗?

【问题讨论】:

  • 你想达到什么目的?
  • 米奇:一个准确的方法来证明你有 2/3 的获胜机会,前提是你交换门
  • 数学证明。经验数据永远不能用作证明,它可以用作证据或支持。

标签: python language-agnostic probability


【解决方案1】:

蒙蒂从不开车开门——这就是节目的重点(他不是你的朋友,他知道每扇门后面的东西)

【讨论】:

  • 不——他从不开车开门!
  • 对不起,它通常是相反的说法——重要的一点(大多数人在采访中使用它时都忽略了这一点)是蒙蒂没有选择随机数
  • 虽然和问题有点无关,但一直被这个问题困扰。结果可能“令人惊讶”的原因只是因为大多数人误解了规则。蒙蒂正在消除一个糟糕的选择,因为他知道答案。如果你能看到这一点,那么这个问题就不再那么有趣了。问题主要在于理解它自己的规则,而不是理解概率。
【解决方案2】:

您提到所有选择都是硬编码的。但是如果您仔细观察,您会发现您认为的“选择”实际上根本不是选择。蒙蒂的决定不失一般性,因为他总是选择后面有山羊的门。你的交换总是取决于蒙蒂的选择,因为蒙蒂的“选择”实际上不是一个选择,你也不是。你的模拟给出了正确的结果..

【讨论】:

  • 事实上,如果您从严格的模拟开始,然后逐步优化,您最终会得到 67% 的时间返回 true 的代码。这也许是让某人相信这个问题的最好方法之一。让他们编写模拟程序。他们会发现他们正在编写愚蠢和冗余的代码。
【解决方案3】:

您的解决方案很好,但如果您想要更严格地模拟所提出的问题(以及更高质量的 Python;-),请尝试:

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

典型的输出是:

Changing has 66721 wins and 33279 losses
IOW, by changing you win 66.7% of the time

【讨论】:

  • 我不确定我理解为什么你有 for k in sequence 部分?您甚至不选择 k,蒙蒂选择哪个门都无关紧要……重要的是“n”,对吗?
  • @Tom,我只是想非常忠实地模拟经典的蒙蒂霍尔问题陈述——蒙蒂选择一扇门(与您原来的选择不同,而不是与汽车的选择不同),您要么改变或者你不改变。是的,蒙蒂在给定约束内的移动是无关紧要的(正如 k 没有出现在循环尾部的事实所示;-),因此可能会删除整个块,除非可执行伪代码的整个点大概是为了帮助说服怀疑者,所以,我们越接近问题的本质越好!-)
  • @Alex:你可能有兴趣阅读我在 Mitch Wheat 的帖子上的 cmets :-)。我试图解释为什么 monty hall 是直观的。
  • @Tom,很好的尝试,但是当她给出正确答案时,对 Marylin vos Savant 的愤怒——包括数学博士对她的抨击——凭经验但无可辩驳地证明了“直觉”不是一个正确的词来描述概率和人脑之间的相互作用!-)
  • @Alex:也许直觉是错误的词……但我认为这是解释为什么它会以这种方式运行的好方法……无需向任何人展示公式或模拟。我用这种方式向人们解释它(使用 100、1000 甚至 10^6 扇门),他们似乎明白了。但是是的,它确实让很多人感到困惑,我第一次看到这个问题时当然没有得到它......但是在知道答案并提出一个更简单的解释之后访问一个问题并提出一个更简单的解释 - 这就是什么我正在尝试做。
【解决方案4】:

我喜欢这样的东西。


#!/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

【讨论】:

    【解决方案5】:

    这是一个交互式版本:

    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
    

    【讨论】:

      【解决方案6】:

      我用列表理解来模拟问题的解决方案

      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))
      

      打印模拟(N)

      【讨论】:

        【解决方案7】:

        这是我觉得最直观的不同变体。希望这会有所帮助!

        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)
        

        学习仍然是一样的,切换会增加您获胜的机会,从上面的运行来看,0.665025416127 与 0.33554730611 相比。

        【讨论】:

          【解决方案8】:

          不是我的样本

          # -*- 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
          

          【讨论】:

            【解决方案9】:

            这是我之前做的一个:

            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)
            

            我也有代码多次运行游戏,并存储了这个和示例输出in this repostory

            【讨论】:

              【解决方案10】:

              这是我在 python 中实现的 MontyHall 问题的解决方案。

              此解决方案利用 numpy 来提高速度,它还允许您更改门的数量。

              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
              

              【讨论】:

                【解决方案11】:

                这是我的版本...

                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)) + '%'
                

                【讨论】:

                  【解决方案12】:

                  我发现这是解决问题最直观的方法。

                  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)
                  

                  这会输出类似

                  If the participant knows_bayes she wins 66 % of the time.
                  If the participant does NOT knows_bayes she wins 33 % of the time.
                  

                  【讨论】:

                    【解决方案13】:

                    今天阅读有关著名的蒙蒂霍尔问题的一章。这是我的解决方案。

                    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()
                    

                    【讨论】:

                      【解决方案14】:

                      更新的解决方案

                      更新,这次使用enum 模块。同样,为了简洁,同时使用 Python 最具表现力的特性来解决手头的问题:

                      from enum import auto, Enum
                      from random import randrange, shuffle
                      
                      class Prize(Enum):
                          GOAT = auto()
                          CAR = auto()
                      
                      items = [Prize.GOAT, Prize.GOAT, Prize.CAR]
                      num_trials = 100000
                      num_wins = 0
                      
                      # Shuffle prizes behind doors. Player chooses a random door, and Monty chooses
                      # the first of the two remaining doors that is not a car. Then the player
                      # changes his choice to the remaining door that wasn't chosen yet.
                      # If it's a car, increment the win count.
                      for trial in range(num_trials):
                          shuffle(items)
                          player = randrange(len(items))
                          monty = next(i for i, p in enumerate(items) if i != player and p != Prize.CAR)
                          player = next(i for i in range(len(items)) if i not in (player, monty))
                          num_wins += items[player] is Prize.CAR
                      
                      print(f'{num_wins}/{num_trials} = {num_wins / num_trials * 100:.2f}% wins')
                      

                      以前的解决方案

                      另一个“证明”,这次是使用 Python 3。注意使用生成器来选择 1) Monty 打开的门,以及 2) 玩家切换到的门。

                      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))
                      

                      【讨论】:

                        【解决方案15】:

                        我刚刚发现,全球胜率是50%,败率是50%……就是根据最终选择的选项,输赢的比例是怎样的。

                        • %Wins(留下):16.692
                        • %Wins(切换):33.525
                        • %Losses (staying) : 33.249
                        • %Losses (switching) : 16.534

                        这是我的代码,与您的不同 + 带有注释的 cmets,因此您可以通过小迭代运行它:

                        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))
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2018-02-13
                          • 1970-01-01
                          • 2021-03-29
                          • 2011-05-04
                          • 2011-02-01
                          • 2021-09-03
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多