据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus 和他的朋友并不想玩这游戏,于是Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。他是怎么计算16和31这两个位置的?
问题比较简单,也比较有意思.可用代数来解决此问题.
假设,
人的序号:1,2,3,4,5,6,7,8,9,10,……,38,39,40,41
对应数组:0,0,0,0,0,0,0,0,0, 0, ……,0,0,0,0
每轮数数的时候,只数0,如0,1,2,0,0,那么这一轮,最后一个0才是一轮的结束.因为前面两个已经自杀了,再数他就违反游戏规则了.
按照这个规则,我们可用设计一个循环,来进行数数和标记
for(int count = 1, pos = -1; count <= number; count++) { //1 int i = 0; //2 while(i != per) { //3 pos = (pos+1) % number; //4 if(man[pos] == 0) //5 i++; //6 } man[pos] = count; //7 }
序号1代表循环41次,因为41个人嘛
序号2代表,数数的当前值
序号3代表,当数数的值等于每轮技术值(per)就结束循环,也就是说,当数数值=3,那么一轮结束.
序号4,当前位置pos是依次递增的,所以当他到41时候,下一个应该是0
序号5,当遇到的人没死,也就是值=0,才能,数数值才能+1
序号7,当一轮结束后,标记最后数数的那个人(pos位置)为第count个自杀的人
public class Josephus { public static int[] josephus(int number, int per) { int[] man = new int[number]; for(int count = 1, pos = -1; count <= number; count++) { int i = 0; while(i != per) { pos = (pos+1) % number; if(man[pos] == 0) i++; } man[pos] = count; } return man; } public static void main(String[] args) { int[] man = josephus(41, 3); System.out.println("约琴夫排列:"); for(int m : man) System.out.print(m + " "); System.out.println("\nL表示3个存活的人要放的位置:"); for(int i = 0; i < 41; i++) { System.out.printf(man[i] <= 41 - 3 ? "D" : "L"); System.out.printf((i+1) % 5 == 0 ? " " : ""); } } }