約瑟夫環問題

2021-07-09 09:27:21 字數 1971 閱讀 4334

最近看到一道題目,也就是約瑟夫環的變體,題目如下:乙隻貓抓住了n只老鼠,其將老鼠排成一圈,依次按照1~m報數,報m值的吃掉,直到只剩下乙隻老鼠時,貓將其放生,求獲生的老鼠編號。

1)按照題目意思,直接採用列表記錄所有老鼠編號,然後依次模擬篩選過程,則可得到逃生的老鼠編號,**如下:

def getlastout(n, m):

"""編號n只老鼠從1~m報數,為m的出列,最後留下的編號

"""rats = list(range(1,n+1))

curr_index = 0

for i in range(n-1):

curr_index += m - 1

curr_index = curr_index % len(rats)

remove_value = rats[curr_index]

del rats[curr_index]

#print('remove index:{}, value:{}, left:{}'.format(curr_index, remove_value, rats[curr_index:]+rats[:curr_index]))

return rats[0]

2)再進一步思考一下,對於每個n吃掉乙隻老鼠後不就是n-1麼,因此可得其遞迴演算法:

def rescuegetlastout(n, m):

"""遞迴方法,考慮去掉第乙個編號之後,其排列順序相當於[m+1 ... n 1 ... m-1],與[1 ... n-1]相對應,可以直接進行變換

"""if n == 2:

return 2 if m % 2 else 1

else:

v = rescuegetlastout(n-1, m)

#print('rescuegetlastout({}, {}): {}'.format( n - 1, m, v))

#v-1先轉變為0~n-1便於%n,後續再+1變回1~n編號

return (v - 1 + m) % n + 1

根據遞迴演算法的思想,很容易可以寫出其對應的迭代版本, 如下:

def getlastout(n, m):

"""根據遞迴方法而來的迭代方法,考慮去掉第乙個編號之後,其排列順序相當於[m+1 ... n 1 ... m-1],與[1 ... n-1]相對應,可以直接進行變換

"""if n < 2:

return 1

live_rat = 1 if m % 2 else 0

for i in range(3, n+1):

live_rat = (live_rat + m) % i

#前面是按照0~i-1編號,返回時改為1~n

return live_rat + 1

3)對於這種問題,往往會思考一下其是否有特定的公式直接進行計算,針對其m值沒有特定的公式,但是當m值為2的時候,是有公式可以計算的,如下:

def getlastoutfor2(n):

"""針對2進行的特殊判斷

"""import math

log_v = int(math.log(n, 2))

return 2*(n - 2**log_v) + 1

這是因為針對任何 2**k 的數,其必定會為 1,而其後面每新增一位將導致整體向右移動兩位(偶數字第一次被去掉),如下:

2**k   [1 2 3 ... 2**k] ->[1 3 ...        2**k-1] -> 1

2**k+1 [1 2 ... 2**k+1] ->[3 5 ... 2**k-1 2**k+1] -> 3

2**k+2 [1 2 ... 2**k+2] ->[1 3 ... 2**k-1 2**k+1] ->[5 ... 2**k-1 2**k+1 1] -> 5

約瑟夫問題 約瑟夫環

約瑟夫 問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死...

約瑟夫問題 約瑟夫環

約瑟夫問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也...

約瑟夫環問題

約瑟夫環問題 問題描述 編號是1,2,n的n個人按照順時針方向圍坐一圈,每個人持有乙個密碼 正整數 一開始任選乙個正整數作為報數上限值m,從第乙個人開始順時針方向自1開始順序報數,報到m時停止報數。報m的人出列,將他的密碼作為新的m值,從他在順時針方向的下乙個人開始重新從1報數,如此下去,直到所有人...