100 Prisoners Dilemma – Python code isn't working

Question:

I stumbled across an interesting probability problem about half an hour ago. https://en.wikipedia.org/wiki/100_prisoners_problem

Essentially, there are 100 boxes, drawers, etc, each with a unique number between 1-100 inside of them. There are also 100 prisoners. Each prisoner has 50 chances to find the box with their number. If even one does not, they all fail. The chances would be abysmally low if they all randomly picked 50 boxes, but there’s a better strategy.

Each prisoner first opens the box labeled with their own number.
If this box contains their number, they are done and were successful.
Otherwise, the box contains the number of another prisoner, and they next open the drawer labeled with this number.
The prisoner repeats steps 2 and 3 until they find their own number, or fail because the number is not found in the first fifty opened drawers.
This should increase their chances to above 30 percent with the way everything can fall into a loop

I glanced through this and decided it was interesting enough to quickly code out in python, but I’m getting a really interesting (and probably wrong) result. Could someone take a look at the code?

import random 

def begin(p=False): #print
    prisoners = [i for i in range(100)]
    boxes = prisoners.copy()
    random.shuffle(boxes)  #we now have a list of shuffled boxes. index represents box num


    if p: #prints 
        for i,num in enumerate(boxes): #i=index, num=content of box. Just to print it out
            print(i,num)

def run(p=True): #print
    results ={"Success":False, "NumSucceed":0, "NumFail":0 }
    
    for prisoner in prisoners:             #Try every prisoner
        fail = True                         #check if they fail
        choice = boxes[prisoner]           #initialize choice as box with prisoner num

        for i in range(49):          #49 becaues first choice counts as well
            if choice==prisoner:
                fail = False
                break
            choice = boxes[choice]
        
        if fail:
            results["NumFail"] +=1
        else:
            results["NumSucceed"]+=1    
    
    
    if results["NumSucceed"]==100:
        results["Success"] = True
    
    if p: #just if I choose not to print results
        print(results)
    return results


for i in range(100):
    begin()
    run()

How many ever times I run it I always get a failing result where 17 prisoners succeeded and the rest failed (I randomize the rotation of the boxes each time too). Does anyone know why? I hope I didn’t just make some stupid error somewhere and waste your time lol, if you do look through this I’d appreciate it

Asked By: Arjun

||

Answers:

Couple of notes first.
Make sure you keep locality/scope in mind with variables. You have a begin() function thats used to declare your variables for the entire script, but they aren’t declared globally so when the other method runs, it won’t be able to find the variables to declare in begin()

not sure how you got it to run as is tbh

so let’s scrap the method and just initialize our prisoners and boxes

import random

prisoners = [i for i in range(100)]
boxes = [i for i in range(100)]

now you can put the shuffle in your run method, since only boxes need to be randomized and conceptually, randomizing boxes would be the first thing done each time the prisoner dilemma happens.



def run(p=True): #print
    random.shuffle(boxes)
    results ={"Success":False, "NumSucceed":0, "NumFail":0 }
    # ...

I ran a couple times and got 20-40 out of 100 total runs where all prisoners found their tag

Answered By: smcrowley

nice little riddle there.

I tried to execute your code, but ran into an NameError exception due to a name space problem. Basically both prisoners and boxes within your code are only defined while begin() is running.

I’ve used your solution and modified it a bit:

import random
from operator import countOf

PRISONERS = 100


class PrisonerProblemSolver:

    def __init__(self) -> None:
        # Generate prisoners and boxes
        self.prisoners = list(range(PRISONERS))
        self.boxes = self.prisoners.copy()
        random.shuffle(self.boxes)

        # collector for results
        self.results = {"Success": False, "NumSucceed": 0, "NumFail": 0}

    def solve(self):
        for prisoner in self.prisoners:
            fail = True
            choice = self.boxes[prisoner]  # tries own box first

            for _ in range(49):  # remaining attempts
                if choice == prisoner:
                    fail = False  # will not die
                    break
                choice = self.boxes[choice]  # follows and hopes

            if fail:
                self.results["NumFail"] += 1
            else:
                self.results["NumSucceed"] += 1

        return self.results["NumSucceed"] == PRISONERS


if __name__ == '__main__':

    solver = PrisonerProblemSolver()
    results = []

    # Let's look at 100 prisons
    for _ in range(100):
        solver = PrisonerProblemSolver()
        results.append(solver.solve())

    # how successful were they in dying?
    print(countOf(results, True))

Hope this helps.

Best regards

Answered By: smller