pm21-dragon/exercises/source/exercise-12/3__Monty_Hall_problem.ipynb
2025-01-24 08:15:26 +01:00

11 KiB

None <html> <head> </head>

The Monty Hall problem (Deutsch: Ziegenproblem)

Read about it on Wikipedia or in German as the Ziegenproblem and come back. I'll wait.

In this notebook, you will write a program to simulated the Monty Hall problem.

We are going to make extensive use of the function random.randint(a, b). From its docstring, it returns a random integer in range [a, b], including both end points.

In [1]:
import random
In [2]:
for i in range(10):
    print(random.randint(0,2))
1
2
0
0
2
0
0
0
0
1

Q1

For your first task, create a function called sample_not which takes two positional arguments (call them not1 and not2) and will return a random integer between 0 and 2 (endpoints included) but which is not either of the arguments. The positional arguments will always be integers between 0 and 2 (inclusive).

In [3]:
# Write your function here
def sample_not(not1, not2):
    while True:
        this_guess = random.randint(0,2)
        if this_guess != not1:
            if this_guess != not2:
                return this_guess
In [4]:
# This is a test of the above, do not change this code.
assert sample_not(0,1)==2
assert sample_not(0,2)==1
assert sample_not(1,0)==2
assert sample_not(1,2)==0
assert sample_not(2,0)==1
assert sample_not(2,1)==0
assert sample_not(0,1)==2
assert sample_not(0,0) in (1,2)
assert sample_not(1,1) in (0,2)
assert sample_not(2,2) in (0,1)

Q2

For your next task, consider the following code:

# generate what is behind the doors
doors = ['goat', 'goat', 'goat']
car_door = random.randint(0,2)
doors[car_door] = 'car'

# Now, the guest makes a first guess.
guess1 = random.randint(0,2)

# Now, the host opens a door, which is not the guess or the car.
host_open_door = sample_not(guess1,car_door)

# Based on the variable `do_switch` (which is not set here), the guess will either make a new choice or stay with the original guess.
if do_switch:
    guess2 = sample_not(guess1,host_open_door)
    final_guess = guess2
else:
    final_guess = guess1

# Finally, determine what was behind the door for the final guess.
result = doors[final_guess]

Put this code in a function called run_game which takes a single argument, do_switch, and returns a boolean, indicating a win with True or a loss with False.

In [5]:
# Write your function here
def run_game(do_switch=True):
    # generate what is behind the doors
    doors = ['goat', 'goat', 'goat']
    car_door = random.randint(0,2)
    doors[car_door] = 'car'

    # Now, the guest makes a first guess.
    guess1 = random.randint(0,2)

    # Now, the host opens a door, which is not the guess or the car.
    host_open_door = sample_not(guess1,car_door)

    # Based on the variable `do_switch` (which is not set here), the guess will either make a new choice or stay with the original guess.
    if do_switch:
        guess2 = sample_not(guess1,host_open_door)
        final_guess = guess2
    else:
        final_guess = guess1

    # Finally, determine what was behind the door for the final guess.
    result = doors[final_guess]
    win = result=='car'
    return win
In [6]:
# This is a test of the above, do not change this code.
count = 300
for do_switch in (True, False):
    wins = 0
    for i in range(count):
        this_run_win = run_game(do_switch)
        assert(type(this_run_win)==bool)
        wins += int(this_run_win)
    print('do_switch: %s, wins: %d, count: %d'%(do_switch, wins, count))
    if do_switch:
        assert  abs(wins/count - 2/3) < 0.1
    else:
        assert abs(wins/count - 1/3) < 0.1
do_switch: True, wins: 207, count: 300
do_switch: False, wins: 100, count: 300

Note the above results. You have numerically simulated the Monty Hall problem.

Q3

Make a new game where there are 100 doors and the host opens 98 of them after the initial guess. Make a new function called run_game_100, and any other functions you need, which plays a single round of this game. Again, this function should take an argument do_switch.

In [7]:
# Write your code here
def sample_not_list(n,not_list):
    while True:
        this_guess = random.randint(0,n-1)
        if this_guess not in not_list:
            return this_guess
            
def run_game_100(do_switch=True):
    n_doors = 100
    n_open = 98
    doors = ['goat']*n_doors
    car_door = sample_not_list(n_doors,[])
    doors[car_door] = 'car'

    guess1 = sample_not_list(n_doors,[])
    host_list = []
    for i in range(n_open):
        host_open_door = sample_not_list(n_doors,host_list+[guess1]+[car_door])
        host_list.append(host_open_door)

    if do_switch:
        guess2 = sample_not_list(n_doors,host_list+[guess1])
        final_guess = guess2
    else:
        final_guess = guess1

    result = doors[final_guess]
    win = result=='car'
    return win
In [8]:
# This is a test of the above, do not change this code.
count = 1000
for do_switch in (True, False):
    wins = 0
    for i in range(count):
        this_run_win = run_game_100(do_switch)
        assert(type(this_run_win)==bool)
        wins += int(this_run_win)
    print('do_switch: %s, wins: %d, count: %d'%(do_switch, wins, count))
    if do_switch:
        assert abs(wins/count - 0.99) < 0.02
    else:
        assert abs(wins/count - 0.01) < 0.02
do_switch: True, wins: 992, count: 1000
do_switch: False, wins: 7, count: 1000
</html>