1. 程式人生 > >每日一練 no.18 約瑟夫問題

每日一練 no.18 約瑟夫問題

問題:

據說著名猶太曆史學家 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