约瑟夫环是一个数学的应用问题:已知n我的(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那我的出列;他的下一我的又从1开始报数,数到m的那我的又出列;依此规律重复下去,直到圆桌周围的人所有出列。this
下面就来个实际例子:设计
10(n)个小孩围成一圈,从第1(k)个开始报数:1,2,3,1,2,3,1,2,3......每次报3(m)的小孩退出. 问最后剩下的那个小孩,在以前10人里是第几个?code
这里对应约瑟夫环的变量值分别为:对象
n=10;k=1;m=3;游戏
下面就用面向对象来模拟这个场景,小孩咱们能够当作一个类get
class Child{ }
小孩有哪些属性呢?数学
1.报数(小孩当前应该报什么数字),2.位置(小孩一开始所在第几个位置)it
理论上只有这两个属性,可是既然是模拟现实场景,那么咱们天然而然要考虑到每一个小孩的真实处境io
小孩围成一个圈,那就说明每个小孩两边都有小孩,这个隐藏属性不能忽略了.最后咱们的小孩类就产生了class
// 定义小孩类 class Child{ public Child(int position){ this.position = position; } public int number; // 小孩当前的报数 public final int position; // 小孩的初始位置,固定不变的 public Child beforeChild; // 小孩前一个孩子 public Child nextChild; // 小孩后一个孩子 }
类设计出来了,下面就开始玩游戏.
首先小孩围成一个圈,咱们能够初始化10个Child对象,而后分别设置他们的左右两边的孩子
Child[] children = new Child[n]; // 初始化小孩子,将孩子相互关联起来 // 能够理解为,手拉手围成一个圈 // 第一个孩子跟最后一个孩子拉手 for (int i = 0; i <n; i++) { children[i] = new Child(i+1); if(i > 0){ // 第二我的开始 together(children[i-1],children[i]); } if(i == n-1){ // 最后一个 // 关联到第一个 together(children[i],children[0]); } } // 相互关联,手拉手 private static void together(Child before,Child next){ before.nextChild = next; next.beforeChild = before; }
最后一个孩子天然要跟第一个孩子关联起来.
准备工做作好了,而后开始报数,报3的人退出.这里遇到的问题有:
判断当前报3的孩子
报3的孩子如何退出
接下来的孩子又开始报1
如何判断只剩下一我的
第一个问题好办,直接判断number==3便可
第二个问题,让他退出也就是他两边没有小孩了,换句话说就是他左边的小孩跟他右边的小孩相关联起来.
第三个问题,咱们让他下面的小孩从新开始报1就能够,currentChild.nextChild.number = 1;
第四个问题,若是只剩下一我的的话,小孩的nextChild属性会指向本身,这个思考下不难理解
解决了上述问题,咱们的核心代码也就出来了:
// 取第一个孩子开始报数 Child currentChild = children[k-1]; currentChild.number = 1; // 第一个孩子固然报1 // 循环,一直报数,当剩下1个小孩就停下来 while(true){ // 若是下一个小孩引用的对象是本身说明只剩下一我的了 if(currentChild.nextChild == currentChild){ break; // 中止报数 } // 若是报数是3,把他的上一个小孩跟他的下一个小孩关联起来 // 这意味着他失去关联,退出 if(currentChild.number == m){ together(currentChild.beforeChild,currentChild.nextChild); // 下一个小孩从新报数1 currentChild.nextChild.number = 1; }else if(currentChild.number < m){// 若是不是3,转移到下一个小孩 // 下一个小孩报数+1 currentChild.nextChild.number = (currentChild.number + 1); } // 轮到下一个小孩 currentChild = currentChild.nextChild; } System.out.println("第"+currentChild.position+"个小孩");
到此程序已经设计完毕了,等到循环退出时,咱们只须要打印当前小孩的position属性便可.
// 定义小孩类 class Child { public Child(int position) { this.position = position; } public int number; // 小孩当前的报数 public final int position; // 小孩的初始位置,固定不变的 public Child beforeChild; // 小孩前一个孩子 public Child nextChild; // 小孩后一个孩子 } public class Test { /** * 已知n我的(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那我的出列; * 他的下一我的又从1开始报数,数到m的那我的又出列;依此规律重复下去,直到圆桌周围的人所有出列。 * @param args */ public static void main(String[] args) { int n = 10, k = 1, m = 3; Child[] children = new Child[n]; // 初始化小孩子,将孩子相互关联起来 // 能够理解为,手拉手围成一个圈 // 第一个孩子跟最后一个孩子拉手 for (int i = 0; i < n; i++) { children[i] = new Child(i + 1); if (i > 0) { // 第二我的开始 together(children[i - 1], children[i]); } if (i == n - 1) { // 最后一个 // 关联到第一个 together(children[i], children[0]); } } // 取第一个孩子开始报数 Child currentChild = children[k - 1]; currentChild.number = 1; // 第一个孩子固然报1 // 循环,一直报数,只剩下1个小孩就停下来 while (true) { // 若是下一个小孩引用的对象是本身说明只剩下一我的了 if (currentChild.nextChild == currentChild) { break; // 中止报数 } // 若是报数是3,把他的上一个小孩跟他的下一个小孩关联起来 // 这意味着他失去关联,退出 if (currentChild.number == m) { together(currentChild.beforeChild, currentChild.nextChild); // 下一个小孩从新报数1 currentChild.nextChild.number = 1; } else if (currentChild.number < m) {// 若是不是3,转移到下一个小孩 // 下一个小孩报数+1 currentChild.nextChild.number = (currentChild.number + 1); } // 轮到下一个小孩 currentChild = currentChild.nextChild; } System.out.println("第" + currentChild.position + "个小孩"); // 第436个小孩 } // 相互关联,手拉手 private static void together(Child before, Child next) { before.nextChild = next; next.beforeChild = before; } }
程序运行结果:第4个小孩