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