用面向對象實現約瑟夫環算法

約瑟夫環

約瑟夫環是一個數學的應用問題:已知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的人退出.這裏遇到的問題有:

  1. 判斷當前報3的孩子

  2. 報3的孩子如何退出

  3. 接下來的孩子又開始報1

  4. 如何判斷只剩下一我的

第一個問題好辦,直接判斷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個小孩

相關文章
相關標籤/搜索