1 问题来历
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
2 代码实现
/**
* @author huwenlong
* @date 2019/12/7 17:30
*/
public class Josephus {
@Test
public void test() {
Children children = buildChildren(41);
// 构建环形链表children
System.out.println(children);
play(children, 1, 3, 2);
System.out.println(children);
}
private Children buildChildren(int size) {
Children children = new Children();
for (int i = 1; i <= size; i ++) {
Child child = new Child();
child.no = i;
children.addChild(child);
}
return children;
}
private void play(Children children, int index, int gap, int remain) {
int len = children.length();
if (index > len) {
return;
}
if (len <= remain) {
return;
}
Child temp = children.first;
// temp跳转到index
for (int i = 1; i < index; i ++) {
temp = temp.next;
}
while (len > remain) {
// temp移动到出列位置的前一个
for (int i = 1; i < gap - 1; i ++) {
temp = temp.next;
}
// 踢除temp的后一个孩子.如果踢除了第一个孩子则需要重新指引first
if (Objects.equals(temp.next, children.first)) {
children.first = temp.next.next;
}
System.out.println(temp.next + "出列");
if (Objects.equals(children.first, children.first.next)) {
// 如果只剩一个孩子,直接踢除
children.first = null;
return;
}
// 踢除孩子
temp.next = temp.next.next;
// temp向前走
temp = temp.next;
len --;
}
}
private class Child {
int no;
Child next;
@Override
public String toString() {
return "Child{" +
"no=" + no +
", next=" + next.no +
'}';
}
}
private class Children {
Child first;
void addChild(Child child) {
if (Objects.isNull(first)) {
first = child;
first.next = first;
return;
}
Child temp = first;
while (!Objects.equals(first, temp.next)) {
temp = temp.next;
}
temp.next = child;
child.next = first;
}
public int length() {
Child temp = first;
int len = 0;
while (Objects.nonNull(first)) {
temp = temp.next;
len ++;
if (Objects.equals(temp, first)) {
break;
}
}
return len;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("len:").append(length());
Child temp = first;
while (Objects.nonNull(first)) {
builder.append(", ").append(temp.toString());
temp = temp.next;
if (Objects.equals(temp, first)) {
break;
}
}
return builder.toString();
}
}
}
输出