约瑟夫环问题

来源:互联网 发布:手机uvz阅读器软件 编辑:程序博客网 时间:2024/06/01 10:37

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

在这里我们可以先不考虑从第几个人开始报数,默认是从1开始报数,求出最后退出的人的编号后,再根据开始报数的编号k求,从编号为k的人开始报数,最好退出人的编号

1、非递归求解,利用boolean类型数组,模拟报数的过程,数到m的人赋值为flase

// 传入参数为总人数n和报到m时退出的数,开始为数组默认赋值都为true,返回的数组中只有一个truepublic static boolean[] doCall(int person, int exitNum) {boolean[] persons = new boolean[person];int number = person, key = 0;for (int i = 0; i < person; i++) {persons[i] = true;}while (number != 1) {for (int i = 0; i < person; i++) {if (!persons[i]) {// 已经为false不需要执行,只是保留当前位置continue;} else {key++;if (key % exitNum == 0) {persons[i] = false;number--;}}}}return persons;}
2、递归求解,根据每报到m时,人数就会少1个推理

假设m=2

f(1)=0

f(2)=0=(f(1)+2)%2

f(3)=2=(f(2)+2)%3

...

f(n)=(f(n)+2)%n

public static int byRecursive(int allPersons, int exitNum) {int result = 0;for (int i = 2; i <= allPersons; i++) {result = (result + exitNum) % i;}return result;}

根据从第一个开始叫,算出最后一个未退出的人位置后,再根据第k个开始叫的人,求解最后一个未退出的人位置

// 默认是从第一个人开始叫的,我们还需要根据从第几个人开始叫的来求最后活着的人编号public static int livePersonNum(int startCallNum, int allPersons,int liveNum) {if ((liveNum + startCallNum) > allPersons) {return (liveNum + startCallNum) % allPersons;} else {return liveNum + startCallNum;}}
完整代码如下:

package com.jason.algorithm;import java.util.ArrayList;import java.util.Scanner;/** * 约瑟夫环问题 约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列; * 他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。 *  * @author Jason *  */public class DeletePersonByThree {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {int inputPersonNum = scanner.nextInt();int exitNum = scanner.nextInt();int startCallNum = scanner.nextInt();if (startCallNum > inputPersonNum && startCallNum < 1) {System.out.println("the start number must >1 &<"+ inputPersonNum);return;}if (inputPersonNum < 1) {return;} else {boolean[] persons = doCall(inputPersonNum, exitNum);for (int i = 0; i < persons.length; i++) {if (persons[i]) {System.out.println("live people number :"+ livePersonNum(startCallNum,inputPersonNum, i));}}System.out.println("=============");System.out.println("live people number :"+ livePersonNum(startCallNum, inputPersonNum,byRecursive(inputPersonNum, exitNum)));break;}}}// 默认是从第一个人开始叫的,我们还需要根据从第几个人开始叫的来求最后活着的人编号public static int livePersonNum(int startCallNum, int allPersons,int liveNum) {if ((liveNum + startCallNum) > allPersons) {return (liveNum + startCallNum) % allPersons;} else {return liveNum + startCallNum;}}public static int byRecursive(int allPersons, int exitNum) {int result = 0;for (int i = 2; i <= allPersons; i++) {result = (result + exitNum) % i;}return result;}// 传入参数为总人数n和报到m时退出的数,开始为数组默认赋值都为true,返回的数组中只有一个truepublic static boolean[] doCall(int person, int exitNum) {boolean[] persons = new boolean[person];int number = person, key = 0;for (int i = 0; i < person; i++) {persons[i] = true;}while (number != 1) {for (int i = 0; i < person; i++) {if (!persons[i]) {// 已经为false不需要执行,只是保留当前位置continue;} else {key++;if (key % exitNum == 0) {persons[i] = false;number--;}}}}return persons;}}

0 0