約瑟夫環是一個數學的應用問題:已知n我的(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那我的出列;他的下一我的又從1開始報數,數到m的那我的又出列;依此規律重複下去,直到圓桌周圍的人所有出列。this
下面就來個實際例子:設計
10(n)個小孩圍成一圈,從第1(k)個開始報數:1,2,3,1,2,3,1,2,3......每次報3(m)的小孩退出. 問最後剩下的那個小孩,在以前10人裏是第幾個?code
這裏對應約瑟夫環的變量值分別爲:對象
n=10;k=1;m=3;遊戲
下面就用面向對象來模擬這個場景,小孩咱們能夠當作一個類get
class Child{ }
小孩有哪些屬性呢?數學
1.報數(小孩當前應該報什麼數字),2.位置(小孩一開始所在第幾個位置)it
理論上只有這兩個屬性,可是既然是模擬現實場景,那麼咱們天然而然要考慮到每一個小孩的真實處境io
小孩圍成一個圈,那就說明每個小孩兩邊都有小孩,這個隱藏屬性不能忽略了.最後咱們的小孩類就產生了class
// 定義小孩類 class Child{ public Child(int position){ this.position = position; } public int number; // 小孩當前的報數 public final int position; // 小孩的初始位置,固定不變的 public Child beforeChild; // 小孩前一個孩子 public Child nextChild; // 小孩後一個孩子 }
類設計出來了,下面就開始玩遊戲.
首先小孩圍成一個圈,咱們能夠初始化10個Child對象,而後分別設置他們的左右兩邊的孩子
Child[] children = new Child[n]; // 初始化小孩子,將孩子相互關聯起來 // 能夠理解爲,手拉手圍成一個圈 // 第一個孩子跟最後一個孩子拉手 for (int i = 0; i <n; i++) { children[i] = new Child(i+1); if(i > 0){ // 第二我的開始 together(children[i-1],children[i]); } if(i == n-1){ // 最後一個 // 關聯到第一個 together(children[i],children[0]); } } // 相互關聯,手拉手 private static void together(Child before,Child next){ before.nextChild = next; next.beforeChild = before; }
最後一個孩子天然要跟第一個孩子關聯起來.
準備工做作好了,而後開始報數,報3的人退出.這裏遇到的問題有:
判斷當前報3的孩子
報3的孩子如何退出
接下來的孩子又開始報1
如何判斷只剩下一我的
第一個問題好辦,直接判斷number==3便可
第二個問題,讓他退出也就是他兩邊沒有小孩了,換句話說就是他左邊的小孩跟他右邊的小孩相關聯起來.
第三個問題,咱們讓他下面的小孩從新開始報1就能夠,currentChild.nextChild.number = 1;
第四個問題,若是隻剩下一我的的話,小孩的nextChild屬性會指向本身,這個思考下不難理解
解決了上述問題,咱們的核心代碼也就出來了:
// 取第一個孩子開始報數 Child currentChild = children[k-1]; currentChild.number = 1; // 第一個孩子固然報1 // 循環,一直報數,當剩下1個小孩就停下來 while(true){ // 若是下一個小孩引用的對象是本身說明只剩下一我的了 if(currentChild.nextChild == currentChild){ break; // 中止報數 } // 若是報數是3,把他的上一個小孩跟他的下一個小孩關聯起來 // 這意味着他失去關聯,退出 if(currentChild.number == m){ together(currentChild.beforeChild,currentChild.nextChild); // 下一個小孩從新報數1 currentChild.nextChild.number = 1; }else if(currentChild.number < m){// 若是不是3,轉移到下一個小孩 // 下一個小孩報數+1 currentChild.nextChild.number = (currentChild.number + 1); } // 輪到下一個小孩 currentChild = currentChild.nextChild; } System.out.println("第"+currentChild.position+"個小孩");
到此程序已經設計完畢了,等到循環退出時,咱們只須要打印當前小孩的position屬性便可.
// 定義小孩類 class Child { public Child(int position) { this.position = position; } public int number; // 小孩當前的報數 public final int position; // 小孩的初始位置,固定不變的 public Child beforeChild; // 小孩前一個孩子 public Child nextChild; // 小孩後一個孩子 } public class Test { /** * 已知n我的(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那我的出列; * 他的下一我的又從1開始報數,數到m的那我的又出列;依此規律重複下去,直到圓桌周圍的人所有出列。 * @param args */ public static void main(String[] args) { int n = 10, k = 1, m = 3; Child[] children = new Child[n]; // 初始化小孩子,將孩子相互關聯起來 // 能夠理解爲,手拉手圍成一個圈 // 第一個孩子跟最後一個孩子拉手 for (int i = 0; i < n; i++) { children[i] = new Child(i + 1); if (i > 0) { // 第二我的開始 together(children[i - 1], children[i]); } if (i == n - 1) { // 最後一個 // 關聯到第一個 together(children[i], children[0]); } } // 取第一個孩子開始報數 Child currentChild = children[k - 1]; currentChild.number = 1; // 第一個孩子固然報1 // 循環,一直報數,只剩下1個小孩就停下來 while (true) { // 若是下一個小孩引用的對象是本身說明只剩下一我的了 if (currentChild.nextChild == currentChild) { break; // 中止報數 } // 若是報數是3,把他的上一個小孩跟他的下一個小孩關聯起來 // 這意味着他失去關聯,退出 if (currentChild.number == m) { together(currentChild.beforeChild, currentChild.nextChild); // 下一個小孩從新報數1 currentChild.nextChild.number = 1; } else if (currentChild.number < m) {// 若是不是3,轉移到下一個小孩 // 下一個小孩報數+1 currentChild.nextChild.number = (currentChild.number + 1); } // 輪到下一個小孩 currentChild = currentChild.nextChild; } System.out.println("第" + currentChild.position + "個小孩"); // 第436個小孩 } // 相互關聯,手拉手 private static void together(Child before, Child next) { before.nextChild = next; next.beforeChild = before; } }
程序運行結果:第4個小孩