据说著名犹太历史学家 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和朋友,然后他们就逃过此难了。由此看来,编程是可以救命的,大家一起来学习编程吧