【Algo】約瑟夫問題(Josephus problem / Josephus permutation)
阿新 • • 發佈:2018-12-13
個人圍一圈, 選一個人, 從 1 開始計數, 計到第 個人出局, 剩下的人組成新環, 再次從 1 開始計數, 計到 的人出局. 如此反覆, 直到剩下最後一個人, 作為勝利者. 問, 在遊戲開始時, 排在第幾號位置, 可以保證自己是最後的勝利者?
舉一個特例理解題目, 設 , 則這一圈人是
1 | 2 | 3 | 4 | 5 | 6 | 1 | … |
---|
出局的順序依次是 5
-> 4
-> 6
-> 2
-> 3
, 所以開局時候排在第 位是勝利者.
程式設計求解的直接思路: 用一個 迴圈連結串列 儲存這 個人, data 裡存一個 bool 值, true 表示還在場上, false 表示出局. 然後迴圈遍歷 次, 最後剩下的人就是勝利者. But, 時間複雜度是 , 任何一個數非常大的話, 這個複雜度都是不可接受的.
優化, 重新分析問題, 試圖找到其中的隱含規律. 計算機語言問題描述: 個人(編號),從開始報數,報到的退出,剩下的人繼續從 開始報數。求勝利者的編號。
我們可以確定, 第一個出列的人編號肯定是 , 那麼剩下的 個人組成新的 約瑟夫環, 新環肯定以 () 開始, 新環的排序肯定是 . 把這個新環的序號調整一下, 看做一個新的約瑟夫環
也就是說變成了 個人報數的子問題. 假設我們知道對於這個 個人的子問題, 第 個人是最終的勝利者, 那麼我們再把這個 塞回去就是剛好 個人情況下的解了. 變回去的公式就是 . 因此, 我們知道了 下的解就可以求出 的解, 典型的遞迴問題.
const int n = 68; // total number
const int m = 3; // kill the m_th person
int f = 0; // safe position
int main()
{
for (int i = 1; i <= n; i++)
f = (f + m) % i;
cout << f + 1 << endl; // counts from 1
}
時間複雜度 , 空間複雜度 , 好很多了.
Ref
- 約瑟夫問題 – 百度百科 : 問題的起源, 分析求解, 和不同版本語言實現. 想不到濃眉大眼的百度百科還要這麼優秀的詞條.
- 約瑟夫問題 : 語言實現寫的比較詳細完整