據說著名猶太曆史學家 Josephus有過以下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,直到所有人都自殺身亡爲止。然而Josephus 和他的朋友並不想遵從。首先從一個人開始,越過k-2個人(因爲第一個人已經被越過),並殺掉第k個人。接着,再越過k-1個人,並殺掉第k個人。這個過程沿着圓圈一直進行,直到最終只剩下一個人留下,這個人就可以繼續活着。問題是,給定了和,一開始要站在什麼地方纔能避免被處決?Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲
接下來,我就用Java來模擬這一過程,並得到上文所說的16和31兩個位置,以躲過這場厄運:
import java.util.ArrayList;
import java.util.List;
/**
* @author LiYang
* @ClassName JosephCircle
* @Description 約瑟夫環算法實現
* @date 2019/10/31 16:32
*/
public class JosephCircle {
/**
* 求解約瑟夫環的算法
* @param player 參與人數
* @param interval 報數的間隔
* @return 約瑟夫環的答案順序
*/
public static List<Integer> calculateJosephCircle(int player, int interval){
//排隊圍成圈的隊伍
List<Integer> queue = new ArrayList<Integer>(player);
//最後返回的答案順序
List<Integer> order = new ArrayList<Integer>();
//初始化所有參與者的序號(1到player)
for (int i = 0; i < player; i++) {
queue.add(i+1);
}
//報數的標誌
int flag = 0;
//當參與者還未全部清除
while (queue.size() > 0){
//輪流報數
for (int i = 0; i < queue.size(); i++) {
//報數標誌++
flag++;
//如果數到了不幸的數字
if (flag % interval == 0){
//從當前的隊伍中移除,併到答案隊伍中
order.add(queue.remove(i));
//重要:由於queue移除了當前元素,
//則下一個元素的下標,就是當前元素的下標,
//於是需要i--
i--;
}
}
}
//最後,返回移除者的先後順序
return order;
}
/**
* 運行示例中的約瑟夫環的算法
* @param args
*/
public static void main(String[] args) {
//41個人參與
int player = 41;
//每數到3,就……
int interval = 3;
//根據算法,求出移除者的先後順序
List<Integer> orderList = calculateJosephCircle(player, interval);
//打印結果,看最後的兩個元素,Joseph和朋友應該在第16、31的位置上
System.out.println("順序:" + orderList);
}
}
最後,運行算法,控制檯輸出以下內容(爲了方便查看,輸出內容做了換行處理):
順序:[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39,
1, 5, 10, 14, 19, 23, 28, 32, 37, 41, 7, 13, 20, 26, 34,
40, 8, 17, 29, 38, 11, 25, 2, 22, 4, 35, 16, 31]
我們可以看到,上面的順序,就是不幸者序號的先後順序,3首先遇到不幸,然後是6、9……直到最後還剩三個,也就是上面的最後三個:35、16、31。此時,Joseph和朋友如果在16和31,則最後一個不幸的就是35號。35號也去了之後,就只剩下16號和31號的Joseph和朋友,然後他們就逃過此難了。由此看來,編程是可以救命的,大家一起來學習編程吧