最近看到一道題目,也就是約瑟夫環的變體,題目如下:乙隻貓抓住了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報數,如此下去,直到所有人...