題目:n個人圍成一圈(編號從1到n),從第1個人開始報數,報到m的人出列,從下一個人再重新報數,報到m的人出列,如此下去,直至所有人都出列。求最后一個出列的人的編號(使用遞歸)。
分析:
遞歸問題分3步走:
1、遞歸收斂:由于m是不變的,所以只能通過n將規模不斷縮小
2、找出口:當遞歸收斂到最小單位時,能得到一個出口。即當n=1時,出局者的位置為0
3、找規律:分析已知條件,與我們需要結果的關聯。
因為我們已知最后一輪出局者在最后一輪中的位置,所以我們只需要找到后一輪出局者的位置,在上一輪中所處的位置,依次遞歸最終就能找到最后出局者在第一輪中的位置;
我們知道第一輪出局者在本輪的位置(從0開始)為:f(n, m) = (m-1) % n
且第二輪第0個位置在第一輪中的位置為:m % n(注:疑惑下面有解決)
,
第二輪第一個位置在第一輪中的位置為:(m+1) % n,
...
第二輪第k個位置在第一輪中的位置為:(m+k) % n;
由此可推導出如下結論:
第二輪出局者在第一輪中的位置為:f(n, m) = (f(n-1, m) + m) % n
第三輪出局者在第二輪中的位置為:f(n-1, m) = (f(n-2, m) + m) % n
...
第n輪出局者在第n-1輪中的位置為: f(2, m) = (f(1, m) + m) % n
根據步驟二,出口條件可得到:f(1, m) = 0
根據遞歸性質,第二輪遞歸表達式即我們寫遞歸函數需要用到的表達式,所以最終表達式為:f(n, m) = (f(n-1, m) + m) % n
// 遞歸求最后一個幸存者的編號(從0開始)
func josephus4(n, m int) int {
if n == 1 {
return 0
}
return (josephus4(n-1, m) + m) % n
}
疑惑1:為什么第二輪開始位置在第一輪中的位置為m % n 而不是(m-1) % n + 1?
解答:分兩步:
1、證明(m-1) % n + 1 不符合我們想要的結果;
當m = kn時,即(kn - 1) % n + 1 = n,而初始位置是從0開始的,最大位置為n - 1,很明顯結果超出了。
當m > n && m != kn時或者m < n 時, 都符合我們的結果
2、證明(m+0) % n 符合結果;
當m = kn 時 kn % n = 0 符合結果
當m > n && m != kn 時或者 m < n時,都符合我們的結果
我們要把這n個人看成一個閉環,在m > 0 && n > 0的情況(m - 1) % n + 1 與 m % n 的區別在于前者不可能得到為0的位置,而后者可以。
疑惑2:為什么初始位置排列要從0開始而不從1開始呢?
解答:因為如果m = kn時,取余會得到結果0,位置從0開始比較方便,
位置從1開始分析:
第一輪出局者在本輪的位置為:f(n, m) = m % n,
第二輪第1個位置在第一輪中的位置為:m % n + 1,
...
第二輪第k個位置在第一輪中的位置為:m % n + k,如果結果大于n的話,再次取余即可。
由此可推導出如下結論:
第二輪出局者在第一輪中的位置為:f(n, m) = m % n + f(n-1, m)
第三輪出局者在第二輪中的位置為:f(n-1, m) = m % n + f(n-2, m)
...
第n輪出局者在第n-1輪中的位置為:f(2, m) = m % n + f(1, m)
根據步驟二,出口條件可得到:f(1, m) = 1 注意:因為這里第一個位置是從1開始
所以最終表達式為:f(n, m) = m % n + f(n-1, m) 需要新增條件,如果有一輪中f(n, m) > n 那么 f(n, m) = f(n, m) % n
// 遞歸求最后一個幸存者的編號(從1開始)
func josephus5(n, m int) int {
if n == 1 {
return 1
}
location := m % n + josephus5(n-1, m)
if location > n {
location = location % n
}
return location
}
以上就是我對約瑟夫環算法的理解,如有不當之處,歡迎指教!!