# grab the pseudo-random # generator module in Python: http://blog.doughellmann.com/2010/10/pymotw-random-pseudorandom-number.html import random # now, build a function to create 3 doors, with one car behind them. # this function will return a dictionary with keys A, B, and C, containing associated values 'empty' or 'car'. def create_doors(): doors = {} doors['A'] = 'empty' doors['B'] = 'empty' doors['C'] = 'empty' # randomly choose *one* of the three doors car_is_behind = random.choice(doors.keys()) doors[car_is_behind] = 'car' return doors print create_doors() print create_doors() print create_doors() def always_switch(): # set up the problem doors_d = create_doors() # pick a door doors = ['A', 'B', 'C'] my_choice = random.choice(doors) # remove it from Monty's consideration -- he will never choose this one doors.remove(my_choice) assert len(doors) == 2, "you should only have two doors left..." # now Monty Hall picks a door: while 1: monty_choice = random.choice(doors) if doors_d[monty_choice] != 'car': # he'll never reveal the car! break doors.remove(monty_choice) # now, because you always switch, you're left with monty's non-choice: assert len(doors) == 1, "you should only have one door left..." my_choice = doors[0] if doors_d[my_choice] == 'car': return True # you win! return False # you lose :( def never_switch(): # set up the problem doors_d = create_doors() # pick a door doors = ['A', 'B', 'C'] my_choice = random.choice(doors) doors.remove(my_choice) assert len(doors) == 2, "you should only have two doors left..." # now Monty Hall picks a door: while 1: monty_choice = random.choice(doors) if doors_d[monty_choice] != 'car': # he'll never reveal the car! break doors.remove(monty_choice) # now, because you never switch, you're left with your original choice: assert len(doors) == 1, "you should only have one door left..." # you stick with your original choice: if doors_d[my_choice] == 'car': return True # you win! return False # you lose :( # ok, let's try this out! print always_switch() print always_switch() print always_switch() print never_switch() print never_switch() print never_switch() won_count = 0 N = 1000 for i in range(N): if always_switch(): won_count += 1 print 'With always_switch(), I won', won_count, 'of', N, 'tries' won_count = 0 N = 1000 for i in range(N): if never_switch(): won_count += 1 print 'With never_switch(), I won', won_count, 'of', N, 'tries' # write a function to create M doors, not just 3. def create_multi_doors(M): doors = {} for i in range(M): doors[i] = 'empty' # randomly choose *one* of the three doors car_is_behind = random.choice(doors.keys()) doors[car_is_behind] = 'car' return doors def always_switch2(M): # set up the problem doors_d = create_multi_doors(M) # pick a door doors = doors_d.keys() my_choice = random.choice(doors) doors.remove(my_choice) while len(doors) > 1: door = random.choice(doors) if doors_d[door] != 'car': doors.remove(door) assert len(doors) == 1, doors # now pick the one that's left: my_choice = doors[0] if doors_d[my_choice] == 'car': return True # you win! return False # you lose :( def never_switch2(M): # set up the problem doors_d = create_multi_doors(M) # pick a door doors = doors_d.keys() my_choice = random.choice(doors) doors.remove(my_choice) while len(doors) > 1: door = random.choice(doors) if doors_d[door] != 'car': doors.remove(door) assert len(doors) == 1, doors # ...but we're sticking with our original choice. if doors_d[my_choice] == 'car': return True # you win! return False # you lose :( print always_switch2(3) print always_switch2(100) print always_switch2(100) print always_switch2(100) print never_switch2(100) print never_switch2(100) print never_switch2(100) won_count = 0 N = 1000 M = 3 for i in range(N): if always_switch2(M): won_count += 1 print 'With always_switch2() and M,', 'doors, I won', won_count, 'of', N, 'tries' won_count = 0 for i in range(N): if never_switch2(M): won_count += 1 print 'With never_switch2() and', M, 'doors, I won', won_count, 'of', N, 'tries' import math # calculate the avg & standard deviation def calc_stddev(series): avg = sum(series) / float(len(series)) devs = [ i - avg for i in series ] devs = [ i**2 for i in devs ] stddev = math.sqrt(sum(devs) / float(len(series))) return avg, stddev # run the given function with parameter M (# doors), N times; return average def try_N_times(fn, M, N): won = 0 for i in range(N): if fn(M): won += 1 return won / float(N) import matplotlib.mlab as mlab # Parameters we've made easy to change ################# n_trials = 500 # How many times to gather statics from groups of N samples M = 3 # How many doors to use (originally 3) N = 100 # How many samples from which to gather statistics (like mean of N coin flips) ################# # Try M doors N times; then run n_trials times, and track. always_list = [ try_N_times(always_switch2, M, N) for _ in range(n_trials) ] never_list = [ try_N_times(never_switch2, M, N) for _ in range(n_trials) ] # get the actual and fitted distribution for the 'always switch' decision n1, bins1, patches1 = hist(always_list, facecolor='green', alpha=0.5, range=(0, 1), bins=50, normed=1) always_avg, always_stddev = calc_stddev(always_list) y1 = mlab.normpdf(bins1, always_avg, always_stddev) plot(bins1, y1, 'g--') # get the actual and fitted distribution for the 'never switch' decision n2, bins2, patches2 = hist(never_list, facecolor='red', alpha=0.5, range=(0, 1), bins=50, normed=1) never_avg, never_stddev = calc_stddev(never_list) y2 = mlab.normpdf(bins2, never_avg, never_stddev) # fit plot(bins2, y2, 'r--') # label the plot xlabel('probability of winning') ylabel('distribution of p for multiple trials') title('Monty Hall problem (%d doors)' % M) plot(bins1, y1, 'g', label='never switch') plot(bins2, y2, 'r', label='always switch') axis(ymax=12) legend() xlabel('probability of winning') ylabel('distribution of p for multiple trials') title('Monty Hall problem (%d doors)' % M) print 'average win rate, always switching:', always_avg print 'average win rate, never switch:', never_avg