Advertisement
Guest User

100 Prisoners Problem in Python

a guest
Jul 30th, 2022
656
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.41 KB | None | 0 0
  1. """
  2. My attempt at testing the 100 prisoners problem and the solutions proposed.
  3.  
  4.  
  5. From Wikipedia:
  6. "The 100 prisoners problem is a mathematical problem in probability theory and combinatorics. In this problem, 100 numbered
  7. prisoners must find their own numbers in one of 100 drawers in order to survive. The rules state that each prisoner may open
  8. only 50 drawers and cannot communicate with other prisoners. At first glance, the situation appears hopeless, but a clever
  9. strategy offers the prisoners a realistic chance of survival. Danish computer scientist Peter Bro Miltersen first proposed
  10. the problem in 2003."
  11.  
  12. "If every prisoner selects 50 drawers at random, the probability that a single prisoner finds their number is 50%. Therefore,
  13. the probability that all prisoners find their numbers is the product of the single probabilities, which is
  14. (1/2)100 ≈ 0.0000000000000000000000000000008, a vanishingly small number. The situation appears hopeless."
  15.  
  16. "Surprisingly, there is a strategy that provides a survival probability of more than 30%. The key to success is that the
  17. prisoners do not have to decide beforehand which drawers to open. Each prisoner can use the information gained from the contents
  18. of every drawer they already opened to decide which one to open next. Another important observation is that this way the success
  19. of one prisoner is not independent of the success of the other prisoners, because they all depend on the way the numbers are
  20. distributed.[2]
  21.  
  22. To describe the strategy, not only the prisoners, but also the drawers, are numbered from 1 to 100; for example, row by row
  23. starting with the top left drawer. The strategy is now as follows:[3]
  24.  
  25. Each prisoner first opens the drawer labeled with their own number.
  26. If this drawer contains their number, they are done and were successful.
  27. Otherwise, the drawer contains the number of another prisoner, and they next open the drawer labeled with this number.
  28. 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
  29. opened drawers.
  30. By starting with their own number, the prisoner guarantees they are on the unique sequence of drawers containing their number.
  31. The only question is whether this sequence is longer than fifty drawers."
  32.  
  33. "The reason this is a promising strategy is illustrated with the following example using 8 prisoners and drawers, whereby each
  34. prisoner may open 4 drawers. The prison director has distributed the prisoners' numbers into the drawers in the following fashion:
  35.  
  36. number of drawer 1 2 3 4 5 6 7 8  
  37. number of prisoner 7 4 6 8 1 3 5 2
  38.  
  39. The prisoners now act as follows:
  40.  
  41. Prisoner 1 first opens drawer 1 and finds number 7. Then they open drawer 7 and find number 5. Then they open drawer 5, where
  42. they find their own number and are successful.
  43. Prisoner 2 opens drawers 2, 4, and 8 in this order. In the last drawer they find their own number 2.
  44. Prisoner 3 opens drawers 3 and 6, where they find their own number.
  45. Prisoner 4 opens drawers 4, 8, and 2, where they find their own number. This is the same cycle encountered by prisoner 2, but
  46. they aren't aware of it.
  47. Prisoners 5 to 8 will also each find their numbers in a similar fashion.
  48. In this case, all prisoners find their numbers. This is, however, not always the case. For example, the small change to the
  49. numbers of swapping drawers 5 and 8 would cause prisoner 1 to fail after opening 1, 7, 5, and 2 (and not finding their own number)"
  50.  
  51.  
  52. "Therefore, using the cycle-following strategy the prisoners survive in a surprising 31% of cases."
  53.  
  54. https://en.wikipedia.org/wiki/100_prisoners_problem
  55. """
  56.  
  57. import random
  58. import time
  59. import sys
  60.  
  61.  
  62. ###################################################
  63. #### SET HERE THE AMOUNT OF TRIES YOU WANT     ####
  64. #### SUGGEST NUMBERS: 1_000, 10_000, 1_000_000 ####
  65. ###################################################
  66.  
  67. num_of_tries: int = 10_000
  68.  
  69.  
  70. #
  71. ##  Creates a list with numbers 1-100, ordered structure
  72. ##  Returns an ordered list of integers
  73. #
  74. def create_ordered_number_list() -> list[int]:
  75.     return list(range(1, 101))
  76.  
  77.  
  78. #
  79. ##  Shuffles the list around with true RNG from systemcall
  80. ##  Returns an unordered list of integers
  81. #
  82. def create_shuffle_list(numbers: list[int]) -> list[int]:
  83.     # create true RNG via system call
  84.     rng = random.SystemRandom()
  85.  
  86.     # copy the ordered list
  87.     new_list = numbers.copy()
  88.  
  89.     # shuffle it around to create a random list
  90.     rng.shuffle(new_list)
  91.  
  92.     return new_list
  93.  
  94.  
  95. #
  96. ## Creates the randomized boxes
  97. ## returns a dict of integers
  98. #
  99. def create_boxes(box_numbers: list[int], papers: list[int]) -> dict[int, int]:
  100.     # create the empty dict
  101.     boxes: dict[int, int] = {}
  102.  
  103.     # helper variable
  104.     counter: int = 0
  105.  
  106.     while counter < 100:
  107.  
  108.         # binds the box_number number to the paper number
  109.         boxes[box_numbers[counter]] = papers[counter]
  110.         counter += 1
  111.  
  112.     return boxes
  113.  
  114.  
  115. #
  116. ## Opens the box with the current number, returns true if the number is found
  117. #
  118. def check_box(boxes: dict[int, int], cur_num: int, box_num: int) -> bool:
  119.     return boxes[box_num] == cur_num
  120.  
  121.  
  122. #
  123. ## Opens the box with the current number, returns the number inside
  124. #
  125. def next_box(boxes: dict[int, int], cur_num: int) -> int:
  126.     return boxes[cur_num]
  127.  
  128.  
  129. #
  130. ## every prisoner can open a maximum of 50 boxes
  131. ## returns true if they find the number within 50
  132. ## tries
  133. #
  134. def open_boxes(boxes: dict[int, int], prisoner: int) -> bool:
  135.     # a maximum of 50 tries, start with 1 for the DEBUG statements
  136.     tries: int = 1
  137.  
  138.     # we start with opening the box with the same number
  139.     # as the prisoner
  140.     box_num: int = prisoner
  141.  
  142.     while tries <= 50:
  143.         # we check if the number inside the box is the number we want
  144.         if check_box(boxes, prisoner, box_num):
  145.  
  146.             # DEBUG: UNCOMMENT TO FOLLOW THE PRISONERS LOOP
  147.             #
  148.             # print(
  149.             #    f"prisoner #{prisoner} found his number in box #{box_num} on try #{tries}"
  150.             # )
  151.             return True
  152.  
  153.         else:
  154.             # DEBUG: UNCOMMENT TO FOLLOW THE PRISONERS LOOP
  155.             # print(
  156.             #    f"({tries}): prisoner #{prisoner} found number #{boxes[box_num]} in box #{box_num}"
  157.             # )
  158.  
  159.             # if not, we go to the next box
  160.             box_num = next_box(boxes, box_num)
  161.  
  162.             # increase the tries
  163.             tries += 1
  164.  
  165.     # if we ran through the whole loop we failed and return False
  166.     return False
  167.  
  168.  
  169. def hundred_prisoners_problem(ordered_list: list[int]) -> bool:
  170.     # see how long the script took to run
  171.     start: time = time.time()
  172.  
  173.     # statistics of failed and passed attempts
  174.     failed: int = 0
  175.     passed: int = 0
  176.  
  177.     # we'll do the loop 10,000 times. The statistics should still work for
  178.     # 100,000, 1,000,000 times etc, with roughly the same percentage
  179.  
  180.     for i in range(num_of_tries):
  181.  
  182.         # the program will take a bit of time when the num_of_tries is above 10_000
  183.         # so we print the current attempt to show progress
  184.         sys.stdout.write("\rAttempt #" + str(i + 1) + "\r")
  185.         sys.stdout.flush()
  186.  
  187.         # for every run we create new randomized prisoners, box numbers and papers
  188.         # and add them into boxes
  189.         prisoners: list[int] = create_shuffle_list(ordered_list)
  190.         box_numbers: list[int] = create_shuffle_list(ordered_list)
  191.         papers: list[int] = create_shuffle_list(ordered_list)
  192.         boxes: dict[int, int] = create_boxes(box_numbers, papers)
  193.  
  194.         # we add a variable to keep track if a prisoner has failed his loop
  195.         # then we break the loop and add it to the failed statistics
  196.         prisoner_failed: bool = False
  197.  
  198.         for prisoner in range(100):
  199.             if open_boxes(boxes, prisoners[prisoner]):
  200.                 continue
  201.             else:
  202.                 prisoner_failed = True
  203.                 break
  204.  
  205.         if prisoner_failed:
  206.             failed += 1
  207.         else:
  208.             passed += 1
  209.  
  210.     end: time = time.time()
  211.     total_time: float = end - start
  212.     sys.stdout.flush()
  213.     print("\r\n")
  214.  
  215.     # we print in seconds because i'm lazy
  216.     print(f"\rThe script took {total_time:.2f} seconds to run")
  217.     print(f"Failed attempts: {failed}, passed attempts: {passed}")
  218.     print(f"The full loop worked about {(passed / num_of_tries) * 100}% of the time")
  219.  
  220.  
  221. lst: list[int] = create_ordered_number_list()
  222. hundred_prisoners_problem(lst)
  223.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement