每日一練 no.18 約瑟夫問題
阿新 • • 發佈:2018-11-26
問題:
據說著名猶太曆史學家 Josephus 有過以下的故事:
在羅馬人佔領橋塔帕特後,39個猶太人與 Josephus 及他的朋友躲到一個洞中,
39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,
由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,
直到所有人都自殺身亡為止。然而 Josephus 和他的朋友並不想自殺,
問他倆安排的哪兩個位置可以逃過這場死亡遊戲?
解答:
模擬推導:
使用collections模組deque進行模擬:
import collections
def ysf(a, b) :
d = collections.deque(range(1, a+1)) # 將每個人依次編號,放入到佇列中
while d:
d.rotate(-b) # 佇列向左旋轉b步
print(d.pop()) # 將最右邊的刪除,即自殺的人
if __name__ == '__main__':
ysf(41,3) # 輸出的是自殺的順序。最後兩個是16和31,說明這兩個位置可以保證他倆的安全。
數學推導:
n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求勝利者的編號。
我們知道第一個人(編號一定是m%n-1) 出列之後,剩下的n-1個人組成了一個新的約瑟夫環(以編號為k=m%n的人開始): k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2,並且從k開始報0。
假設x是最終的勝利者,逆推的話容易得到 x’=(x+k)%n ,可以以此逆推到最開始的位置。
# 遞迴直接求出
def ysf(m,k):
if m == 1:
return 0
else:
return (ysf(m-1, k) +k) % m
ysf(41, 3) + 1
# 遍歷求出結果
def ysf(m, k):
s = 0
for i in range(2, m+1):
s = (s + k) % i
return s
ysf(41, 3) + 1