约瑟夫环

问题描述

约瑟夫环是一个非常经典的数学问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。开始报数,数到数字为k的那个人出列;继续,他的下一个人又从1开始报数,数到k的那个人又出列;依此规律重复下去,问最后出列的是谁?

背景故事 39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓。于是决定了自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀。然后下一个人重新从1报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想死,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

解法1

public static void main(String[] args) { int n = 41; int k = 2; LinkedList<Integer> list = new LinkedList<>(); for (int i = 1; i <= n; i++) { list.add(i); } System.out.println(list); int i = 1; // 开始报数 while (list.size() > 1) { // 循环直到剩最后一人 if (i % k != 0) { // 如果报的不是k的倍数,存活,进入下一轮 list.addLast(list.removeFirst()); } else { // 是k的倍数,被杀 list.removeFirst(); System.out.println(list); // 最后一人为19 } i++; // 报数加1 } }

解法2

/* * 对于总人数n,报数间隔k,f(i) 表示第i轮被杀的人 * 有如下公式: * 注意人的编号从0开始 * f(1) = 0; * f(i) = (f(i-1) + k) % i; (i>1) * * 如果人的编号从1开始,公式要变为 * f(1) = 1; * f(i) = (f(i-1) + k - 1) % i + 1; (i>1) */ public static void main(String[] args) { int n = 41; int k = 2; int f = 0; for (int i = 2; i <= n; i++) { f = (f + k) % i; } System.out.println(f+1); // 最后一人为19 }

注意 解法2 只适用于求最后一人

变体

100个人围成一圈,数到3和3的倍数时出圈,问剩下的人是谁?

解法略

自定义单向链表实现

class List { static class Node { public int value; public Node(int value, Node next) { this.value = value; this.next = next; } public Node next; @Override public String toString() { return "Node(" + value + ")->" + next; } } Node first; Node last; public List(int n) { last = new Node(n, null); Node c = last; for (int i = n - 1; i >= 1; i--) { Node p = new Node(i, c); c = p; if (i == 1) { first = p; } } } // 将第一个节点移动至最后一个 public void movef2l() { Node t = first.next; first.next = null; last.next = first; last = first; first = t; } // 将第一个节点移除 public void removef() { first = first.next; } }
public static void main(String[] args) { int m = 41; int k = 3; List list = new List(m); int i = 1; while (list.first != list.last) { if (i % k != 0) { list.movef2l(); } else { list.removef(); } i++; System.out.println(list.first); } }

results matching ""

    No results matching ""