約瑟夫環問題
問題描述:
n個人(編號
1~n),從1
開始報數,報到
m的退出,剩下的人繼續從
1開始報數。求勝利者的編號。數學解法複雜度:
o(n)。
無論是用鍊錶實現還是用陣列實現都有乙個共同點:要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達
o(nm),當n
,m非常大(
例如上百萬,上千萬
)的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規,實施一點數學策略。
為了討論方便,先把問題稍微改變一下,並不影響原意:
問題描述:
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。
現在我們把他們的編號做一下轉換:
k --> 0
k+1 --> 1
k+2 --> 2
......
k-2 --> n-2
k-1 --> n-1
變換後就完完全全成為了
(n-1)
個人報數的子問題,假如我們知道這個子問題的解:例如
x是最終的勝利者,那麼根據上面這個表把這個
x變回去不剛好就是
nx'=(x+k)%n
如何知道
(n-1)
個人報數的問題的解?對,只要知道
(n-2)
個人的解就行了。
(n-2)
個人的解呢?當然是先求
(n-3)
的情況----
這顯然就是乙個倒推問題!好了,思路出來了,下面寫遞推公式: 令
f[i]表示i
個人玩遊戲報
m退出最後勝利者的編號,最後的結果自然是
f[n]
遞推公式
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)
有了這個公式,我們要做的就是從
1-n順序算出
f[i]
的數值,最後結果是
f[n]
。因為實際生活中編號總是從
1開始,我們輸出
f[n]+1
由於是逐級遞推,不需要儲存每個
f[i]
,程式也是異常簡單:
**如下:
1 #include 2 #include3 int main()
4 14 }
15 system("pause");
16 return 0;
17 }
詳細解釋如下圖: 約瑟夫問題 約瑟夫環
約瑟夫 問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死...
約瑟夫問題 約瑟夫環
約瑟夫問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也...
約瑟夫環問題
約瑟夫環問題 問題描述 編號是1,2,n的n個人按照順時針方向圍坐一圈,每個人持有乙個密碼 正整數 一開始任選乙個正整數作為報數上限值m,從第乙個人開始順時針方向自1開始順序報數,報到m時停止報數。報m的人出列,將他的密碼作為新的m值,從他在順時針方向的下乙個人開始重新從1報數,如此下去,直到所有人...