约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
在这里我们可以先不考虑从第几个人开始报数,默认是从1开始报数,求出最后退出的人的编号后,再根据开始报数的编号k求,从编号为k的人开始报数,最好退出人的编号
1、非递归求解,利用boolean类型数组,模拟报数的过程,数到m的人赋值为flase
// 传入参数为总人数n和报到m时退出的数,开始为数组默认赋值都为true,返回的数组中只有一个true
public 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,返回的数组中只有一个true
public 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;
}
}