約瑟夫斯置換是一個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中，類似問題又被稱為約瑟夫環。 人們站在一個等待被處決的圈子裡。 計數從圓圈中的指定點開始，並沿指定方向圍繞圓圈進行。 在跳過指定數量的人之後，處刑下一個人。 對剩下的人重複該過程，從下一個人開始，朝同一方向跳過相同數量的人，直到只剩下一個人，並被釋放。 問題即，給定人數、起點、方向和要跳過的數字，選擇初始圓圈中的位置以避免被處決。 歷史 這個問題是以 zhhant 弗拉維奧約瑟夫斯；zhhans 弗拉維奧約瑟夫 命名的，他是 1 世紀的一名猶太曆史學家。他在自己的日記中寫道，他和他的 40 個戰友被羅馬軍隊包圍在洞中。他們討論是自殺還是被俘，最終決定自殺，並以抽籤的方式決定誰殺掉誰。約瑟夫斯和另外一個人是最後兩個留下的人。約瑟夫斯說服了那個人，他們將向羅馬軍隊投降，不再自殺。約瑟夫斯把他的存活歸因於運氣或天意，他不知道是哪一個。The War of the Jews 3387391 解法 比較簡單的做法是用迴圈單鏈表模擬整個過程，時間複雜度是 O nm。如果只是想求得最後剩下的人，則可以用數學推導的方式得出公式。且先看看模擬過程的解法。 Python 版本 coding utf8 class Node object def __init__self value selfvalue value selfnext None def create_linkList n head Node 1 pre head for i in range 2 n1 newNode Node i prenext newNode pre newNode prenext head return head n 5 總的個數 m 2 數的數目 if m 1 如果是 1 的話，特殊處理，直接輸出 print n else head create_linkList n pre None cur head while curnext cur 終止條件是節點的下一個節點指向本身 for i in range m1 pre cur cur curnext print curvalue prenext curnext curnext None cur prenext print curvalue C 版本 include iostream include cstdlib include cstdio using namespace std typedef struct _LinkNode int value struct _LinkNode next LinkNode LinkNodePtr LinkNodePtr createCycle int total int index 1 LinkNodePtr head NULL curr NULL prev NULL head LinkNodePtr malloc sizeof LinkNode headvalue index prev head while total 0 curr LinkNodePtr malloc sizeof LinkNode currvalue index prevnext curr prev curr currnext head return head void run int total int tag LinkNodePtr node createCycle total LinkNodePtr prev NULL int start 1 int index start while node nodenext if index tag printf dn nodevalue if tag start prev nodenext nodenext NULL node prev else prevnext nodenext nodenext NULL node prevnext index start else prev node node nodenext index int main if argc 3 return 1 run atoi argv 1 atoi argv 2 return 0 數學推導解法 我們將明確解出 k2 時的問題。對於 kneq 2 的情況，我們在下面給出一個一般的解法。 設 f n 為一開始有 n 個人時，生還者的位置 注意：最終的生還者只有一個。走了一圈以後，所有偶數號碼的人被殺。再走第二圈，則新的第二、第四、 個人被殺，等等；就像沒有第一圈一樣。如果一開始有偶數個人，則第二圈時位置為 x 的人一開始在第 2x 1 個位置。因此位置為 f 2n 的人開始時的位置為 2f n 1。這便給出了以下的遞推公式： f 2n2f n1 如果一開始有奇數個人，則走了一圈以後，最終是號碼為 1 的人被殺。於是同樣地，再走第二圈時，新的第二、第四、 個人被殺，等等。在這種情況下，位置為 x 的人原先位置為 2x1。這便給出了以下的遞推公式： f 2n12f n1 如果我們把 n 和 f n 的值列成表，我們可以看出一個規律： n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 f n 1 1 3 1 3 5 7 1 3 5 7 9 11 13 15 1 從中可以看出，f n 是一個遞增的奇數數列，每當 n 是 2 的冪時，便重新從 f n1 開始。因此，如果我們選擇 m 和 l，使得 n2ml 且 0leq l2m，那麼 f n2 cdot l1。注意：2m 是不超過 n 的最大冪，l 是留下的量。顯然，表格中的值滿足這個方程。我們用數學歸納法給出一個證明。 定理：如果 n2ml 且 0leq l2m，則 f n 2l1。 證明：對 n 應用數學歸納法。n1 的情況顯然成立。我們分別考慮 n 是偶數和 n 是奇數的情況。 如果 n 是偶數，則我們選擇 l_1 和 m_1，使得 n2 2m_1l_1，且 0leq l_1 2m_1。注意 l_1 l2。我們有 f n 2f n212 2l_11 12l1，其中第二個等式從歸納假設推出。 如果 n 是奇數，則我們選擇 l_1 和 m_1，使得 n12 2m_1l_1，且 0leq l_1 2m_1。注意 l_1 l12。我們有 f n 2f n1212 2l_11 12l1，其中第二個等式從歸納假設推出。證畢。 答案的最漂亮的形式，與 n 的二進位制表示有關：把 n 的第一位移動到最後，便得到 f n。如果 n 的二進位制表示為 nb_0 b_1 b_2 b_3dots b_m，則 f nb_1 b_2 b_3 dots b_m b_0。這可以透過把 n 表示為 2ml 來證明。 一般情況下，考慮生還者的號碼從 n1 到 n 的變化，我們可以得到以下的遞推公式 編號從 0 開始： f nkf n1kk bmod n，f 1k0 這種方法的執行時間是 O n。 程式實現（C） include iostream using namespace std 編號從 0 開始，也就是說如果編號從 1 開始結果要加 1 int josephus int n int k 非遞回版本 int s 0 for int i 2 i n i s s k i return s int josephus_recursion int n int k 遞回版本 return n 1 josephus_recursion n 1 k k n 0 int main for int i 1 i 100 i cout i josephus i 5 josephus_recursion i 5 endl return 0 對於 kn，可以將上述方法推廣，將殺掉第 k、2k、、lfloor nk rfloor 個人視為一個步驟，然後把號碼改變，可得如下遞推公式，執行時間為 O klog n。 f nk begin cases 0 text if n1 f n1kk bmod n text if 1 n k left lfloor frac k f nkn bmod k bmod nk1 right rfloor text where n n left lfloor frac nk right rfloor text if k le n end cases 程式實現（C） include cstdio using namespace std 編號從 1 開始，結果要加 1 int josephus int n int k if k 1 return n 1 int ans 0 for int i 2 i n if ans k i ans ans k i i continue int step i 1 ans 1 k 1 向下取整 if i step n ans n i 1 k break i step ans step k return ans int main int n k while scanf d d n k 2 printf dn josephus n k n 1 return 0 註釋 參考文獻 Thomas H Cormen Charles E Leiserson Ronald L Rivest and Clifford Stein Introduction to Algorithms Second Edition MIT Press and McGrawHill 2001 ISBN 0262032937 Chapter 14 Augmenting Data Structures pp318 外部連結 cuttheknot 上的約瑟夫斯遊戲 （Java Applet） Category 組合數學 Category 置換 Category 計算機科學基礎理論 Category 數學問題