Search code examples
pythonmontecarlo

A spin game monte carlo


Problem:

Here’s a little Monte Carlo challenge problem, Consider the following game, which uses the two spinner disks. Suppose a player spins one or the other of the pointers on the disks according to the following rules:

  1. if the player spins pointer i and it stops in the region with area p_{ij}, he moves from disk i to disk j (i and j are either 1 or 2);

  2. if a pointer stops in the region with area x_i, the game ends;

  3. if the game ends in the region with area x_1, the player wins, but if the pointer stops in the region with area x_2 the player loses.

What is the probability the player, starting with disk 1, wins? Assume the area of each disk is one, so that x_1+p_{11}+p_{12} =1, as well as that x_2+p_{21}+p_{22} =1

Run your code for the case of p_{11} =0.2, p_{12} =0.4, p_{21} =0.3, and p_{22} =0.35.

import random
p_11 = 0.2
p_12 = 0.4 #0.2+0.4
p_21 = 0.3
p_22 = 0.35


wins = 0
pointer = 0
pointer2 = 0
for i in range(10**7):
    while pointer < p_11:
        pointer2 = 0    #resetting pointer2
        pointer = random.uniform(0,1)
        if p_11+p_21  < pointer < 1:  #area corresponding to x_1
            wins += 1  #wins
            pointer = 0  
            break
        else:
            pointer = 0  #resetting pointer1
            while pointer2 < p_22:
                pointer2 = random.uniform(0,1)
                if p_22+p_21 < pointer2 < 1:  #area corresponding to x_2
                    pointer2 = 0
                    break  #loses

print(wins/10**7)

The correct answer is 0.5821 however I am getting 0.7141465. Where am I doing wrong ?

I edited my code, in this case it turns the disk again for p_22 and p_11 cases

enter image description here

The question is from the book called Digital Dice (Paul J. Nahim) Page 27-29 (Theres a pdf )


Solution

  • I have analyzed the problem mathematically and found out that the solution is actually:

    (1 - p_11 - p_12) * (1 - p_22) / ((1 - p_11) * (1 - p_22) - p_12 * p_21) (which is actually not correct in some corner cases (e.g. p_22 = 1))

    This is actually written in Appendix 6 of the Digital Dice book, so I won't prove it.

    With your numbers it gives the answer of 0.65 and that is correct. Your code changed a lot and now it gives the output of 1.0 instead of what is written in the question. Here I corrected the first version of your code:

    import random
    
    
    p_11 = 0.2
    p_12 = 0.4
    p_21 = 0.3
    p_22 = 0.35
    
    total_iterations = 10 ** 6
    
    wins = 0
    num = 0
    for i in range(total_iterations):
        current_disk = 1
        while True:
            num = random.uniform(0, 1)
            if current_disk == 1:
                if num < p_12:
                    current_disk = 2
                    continue
                elif num > p_11 + p_12:
                    wins += 1  #wins
                    break
            else:
                if num < p_21:
                    current_disk = 1
                    continue
                elif num > p_21 + p_22:
                    break
    
    print(wins / total_iterations)
    print((1 - p_11 - p_12) * (1 - p_22) / ((1 - p_11) * (1 - p_22) - p_12 * p_21))
    

    Now about your current code. It is wrong now because break # loses breaks from the loop while pointer2 < p_22, not from the loop while pointer < p_11. We can fix it by adding extra flag lost and that will give you correct answer.

    import random
    p_11 = 0.2
    p_12 = 0.4 #0.2+0.4
    p_21 = 0.3
    p_22 = 0.35
    
    
    wins = 0
    pointer = 0
    pointer2 = 0
    for i in range(10**6):
        while pointer < p_11:
            pointer2 = 0    #resetting pointer2
            pointer = random.uniform(0,1)
            if p_11+p_21  < pointer < 1:  #area corresponding to x_1
                wins += 1  #wins
                pointer = 0  
                break
            else:
                pointer = 0  #resetting pointer1
                lost = False
                while pointer2 < p_22:
                    pointer2 = random.uniform(0,1)
                    if p_22+p_21 < pointer2 < 1:  #area corresponding to x_2
                        pointer2 = 0
                        lost = True
                        break  #loses
                if lost:
                    break
    
    print(wins/10**6)