約瑟夫問題 java 實現

SylArmenta 8年前發布 | 2K 次閱讀 Java

約瑟夫問題

這是17世紀的法國數學家加斯帕在《數目的游戲問題》中講的一個故事:15個教徒和15 個非教徒在深海上遇險,必須將一半的人投入海中,其余的人才能幸免于難,于是想了一個辦法:30個人圍成一圓圈,從第一個人開始依次報數,數到第九個人就將他扔入大海。該人后面的人從1開始重新報數,如此循環進行直到僅余15個人為止。問怎樣排法,才能使每次投入大海的都是非教徒。

思路
1. 先建立一個類,有 id 和 isRemove

class Person {
    int id;
    boolean isRemove;

    public Person(int id, boolean isRemove) {
        this.id = id;
        this.isRemove = isRemove;
    }

    public boolean isRemove() {
        return isRemove;
    }

    public void setRemove(boolean isRemove) {
        this.isRemove = isRemove;
    }
}
    2.
class Circle {
    private ArrayList<Person> circle = new ArrayList<Person>();

    private int amount; // 一共多少人

    Circle(int amount) {
        this.amount = amount;
        for (int i = 0; i < amount; i++) {
            Person p = new Person(i + 1, false);
            circle.add(p);
        }
    }

    /** * * @param index * 最開始扔人的位置,比如第9人 * @param total * 共需要扔幾次 */
    public void getIndex(int index, int total) {

        // 起始位置
        int currentIndex = -1;

        for (int t = 0; t < total; t++) {
            int notRemove = 0;

            //本來是notRemove != 9,寫死不好
            while (notRemove != index) {
                currentIndex++;
                // 或者用 currentIndex % amount 解決
                if (currentIndex == amount) {
                    currentIndex = 0;
                }
                if (!circle.get(currentIndex).isRemove) {
                    notRemove++;
                }
            }

            // 將扔的人設為 true
            Person p = circle.get(currentIndex);
            p.setRemove(true);
            circle.set(currentIndex, p);

            System.out.printf("第 %-2d 次仍的人id是%4d\n", t + 1, p.id);

        }

    }

}

代碼建立一個容器來保存各個人,主要就是不移除這個人,而是把他狀態設為setRemove(true),好處這樣容器的大小保持不變。如果刪掉這樣人,容器大小每次減1,算起來麻煩

if (currentIndex == amount) { currentIndex = 0; }

本來是用 currentIndex % amount 實現的,不過本代碼都是+1,肯定會有 == amount的情況,用置為0更好

結果

第 1  次仍的人id是   9
第 2  次仍的人id是  18
第 3  次仍的人id是  27
第 4  次仍的人id是   6
第 5  次仍的人id是  16
第 6  次仍的人id是  26
第 7  次仍的人id是   7
第 8  次仍的人id是  19
第 9  次仍的人id是  30
第 10 次仍的人id是  12
第 11 次仍的人id是  24
第 12 次仍的人id是   8
第 13 次仍的人id是  22
第 14 次仍的人id是   5
第 15 次仍的人id是  23

優化

也可以用數組實現,下標和 boolean 正好模擬上面的 Person 類

static void other() {
    boolean[] usaJapa = new boolean[30];
    // 用類庫初始化
    Arrays.fill(usaJapa, true);

    int leftCount = usaJapa.length;

    int countNum = 0;
    int index = 0;
    int i = 0;
    while (leftCount > 15) {
        if (usaJapa[index]) {
            countNum++;
        }

        if (countNum == 9) {
            countNum = 0;
            usaJapa[index] = false;
            leftCount--;
            System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, index + 1);

        }

        index++;
        if (index == usaJapa.length) {
            index = 0;
        }
    }

}

用鏈表實現,remove 其中的數據

class Circle {
    private LinkedList<Person> circle = new LinkedList<Person>();

    private int amount; // 一共多少人

    Circle(int amount) {
        this.amount = amount;
        for (int i = 0; i < amount; i++) {
            Person p = new Person(i + 1, false);
            circle.add(p);
        }
    }

    public void othergetIndex(int mark, int total) {
        // remain 是剩下的人數,total 是需要刪除的人數
        int remain = amount;
        int count = 0;
        int current = 0;
        int i = 0;
        while (remain > amount - total) {
            count++;
            // 注意這人如果達到 mark,比如第9個人
            // 刪掉第9個人后,再刪第9個人,就是刪原來的第10個人
            if (count == mark) {
                // remove 返回的是刪除的 Person
                Person p = circle.remove(current);
                System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, p.id);
                count = 0;
                remain--;
            } else {
                current++;
            }
            if (current == amount - i) {
                current = 0;
            }

        }

    }
}

用 LinkedList 刪除操作更快,

Person p = circle.remove(current);
remove 返回刪除的元素

LinkedList<Integer> testList = new LinkedList<Integer>();
for (int i = 0; i < 10; i++) {
        testList.add(i);
}
for (int i = 0; i < 5; i++) {
    System.out.println(testList.remove(3));
}   /** * 3 * 4 * 5 * 6 * 7 */

這段代碼的輸出可以看出,刪除了第3個元素,后面元素往前移動

 本文由用戶 SylArmenta 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!