約瑟夫環問題(josephus problem)詳解
阿新 • • 發佈:2019-01-07
約瑟夫環問題描述:
編號為1,2,3…n的人一詞圍成一圈,從第k個人開始報數(從1開始),數到m的人退出。接著下一個人又從1開始報數,數到m的人退出,以此類推。問:剩下的人的編號是多少?
如:n=6,m=3,k=1
原始序列: 1 2 3 4 5 6 ; 從編號1開始報數
第一輪數完後的序列為: 1 2 4 5 ; 3、6出列——從編號1開始報數
第二輪數完後的序列為: 1 2 5 ; 4出列——從編號5開始報數
第三輪數完後的序列為: 1 5 ; 2 出列——從5開始報數
第四輪數完後的序列為: 1 ;5 出列
所以,最後出列的編號為1.
求解方法一:模擬報數過程,時間複雜度O(M*N)
該方法的思想是將所有的編號構造成一個環形連結串列,然後移動到編號為k的節點開始報數,數到M的時候移除連結串列節點,接著從下一個節點開始報數,移除數到M的節點。以此類推,直到只剩下一個節點。
public static int josephusOMN(int n, int m) { return josephusOMN(n, m, 1); } public static int josephusOMN(int n, int m, int k) { if (n <= 0 || m <= 0 || k <= 0) return -1; // consruct josephus circle JosephusNode header = new JosephusNode(1); JosephusNode nodes = header; for (int i = 2; i <= n; i++) { nodes.mNext = new JosephusNode(i); nodes = nodes.mNext; } nodes.mNext = header; // end for (int i = 1; i < k; i++) { nodes = nodes.mNext; // move to start off person } while (nodes != nodes.mNext) { for (int i = 1; i < m; i++) { nodes = nodes.mNext; } // System.out.print(nodes.mNext.mNum + " , "); nodes.mNext = nodes.mNext.mNext; } return nodes.mNum; } private static class JosephusNode { public int mNum; public JosephusNode mNext; public JosephusNode(int num) { this(num, null); } public JosephusNode(int num, JosephusNode node) { mNum = num; mNext = node; } }
求解方法二:方便求解最後的勝利者同時也適合列印 出列 序列的,時間複雜度O(N).
下面方法借鑑自Java程式練習-約瑟夫環問題
public static int josepusONWithList(int n, int m) { return josepusONWithList(n, m, 1); } public static int josepusONWithList(int n, int m, int k) { if (n <= 0 || m <= 0 || k <= 0) return -1; LinkedList<Integer> list = new LinkedList<Integer>(); for (int i = 1; i <= n; i++) list.add(i); int outPos; while (list.size() > 1) { outPos = (int) (k + m - 2) % list.size(); // System.out.print(list.get(outPos)+" , "); list.remove(outPos); k = outPos + 1; } // System.out.println(list.get(0)); return list.get(0); }
求解方法三:對模擬方法的改進——數學優化方法,時間複雜度O(N)。具體思想參見約瑟夫環的數學優化方法
public static int josephusON(int n, int m) {
return josephusON(n, m, 1);
}
/**
* Josephus 環的一個O(N)演算法
*
* @param n 總人數
* @param m 數到m的人出列
* @param k 開始報數人的編號
* @return 最後一個出列的編號
*/
public static int josephusON(int n, int m, int k) {
int p = 0;
for (int i = 2; i <= n; i++) {
// System.out.print((p + k == n ? n : (p + k) % n)+" ");
p = (p + m) % i;
}
return p + k == n ? n : (p + k) % n; // 返回最後一人的位置
}
在有時候需要知道出列的序列,上述System.out部分就是出列序列。
約瑟夫環問題求解就先寫到這裡,朋友們若有其他方法也別忘了告訴樓主哦_… 收集到其他求解方法後再補充。