【详解】使用Java解决约瑟夫环问题
使用Java解决约瑟夫环问题
简介
约瑟夫环问题是一个经典的数学问题,描述了n个人围成一圈,从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。这个问题可以通过多种方法解决,本文将介绍一种基于数学公式的高效解法,并提供相应的Java实现。
数学公式解法
约瑟夫环问题可以通过递推公式来求解,其递推公式如下:
\[ f(n, m) = \begin{cases} 0 & \text{if } n = 1 \\ (f(n-1, m) + m) \mod n & \text{if } n > 1 \end{cases} \]
其中:
- \( f(n, m) \) 表示n个人报数m时最后留下的人的位置。
- 初始条件是 \( f(1, m) = 0 \),表示只有一个人时,最后留下的人是第0号(从0开始计数)。
通过这个递推公式,我们可以逐步计算出最后留下的人的位置。
Java实现
下面是使用Java实现上述递推公式的代码:
public class JosephusProblem {
/**
* 计算约瑟夫环问题中最后留下的人的位置
*
* @param n 人数
* @param m 报数的数字
* @return 最后留下的人的位置(从0开始计数)
*/
public static int findLastPerson(int n, int m) {
if (n == 1) {
return 0;
}
return (findLastPerson(n - 1, m) + m) % n;
}
public static void main(String[] args) {
int n = 10; // 人数
int m = 3; // 报数的数字
int lastPerson = findLastPerson(n, m);
System.out.println("最后留下的人是原来的第 " + (lastPerson + 1) + " 号");
}
}
代码解释
- findLastPerson 方法:
- 参数
n 表示总人数。 - 参数
m 表示报数的数字。 - 如果
n 等于1,返回0,因为只有一个人时,最后留下的人是第0号。 - 否则,递归调用
findLastPerson(n - 1, m) 并计算 (f(n-1, m) + m) % n。
- main 方法:
- 定义人数
n 和报数的数字 m。 - 调用
findLastPerson 方法计算最后留下的人的位置。 - 输出结果,注意位置是从0开始计数的,所以输出时加1。
时间复杂度
该算法的时间复杂度为 \( O(n) \),因为每次递归调用都会减少一个元素,直到 n 减少到1为止。空间复杂度为 \( O(n) \),因为递归调用会占用栈空间。

约瑟夫环问题是一个经典的算法问题,可以通过多种方式解决,包括递归、迭代和数学公式。下面我将提供一个使用Java实现的解决方案,该方案基于数学公式,时间复杂度为O(1)。
数学公式解法
对于约瑟夫环问题,可以使用以下递推公式来解决:
\[ f(n, m) = (f(n-1, m) + m) \mod n \]
其中:
- \( n \) 是人数。
- \( m \) 是报数的步长(在这个问题中,m=3)。
- \( f(n, m) \) 表示最后留下的人的编号(从0开始计数)。
最终结果需要转换为从1开始计数的编号。
Java 实现
public class JosephusProblem {
// 计算最后留下的人的编号(从1开始计数)
public static int findLastPerson(int n, int m) {
// 初始化,当只有一个人时,最后留下的人是0号
int lastPerson = 0;
// 从2人开始,逐步增加到n人
for (int i = 2; i <= n; i++) {
// 根据递推公式计算
lastPerson = (lastPerson + m) % i;
}
// 转换为从1开始计数的编号
return lastPerson + 1;
}
public static void main(String[] args) {
int n = 10; // 人数
int m = 3; // 报数的步长
int result = findLastPerson(n, m);
System.out.println("最后留下的人是原来的第 " + result + " 号");
}
}
解释
- 初始化:当只有一个人时,最后留下的人是0号(从0开始计数)。
- 递推计算:从2人开始,逐步增加到n人,每次根据递推公式计算当前人数下的最后留下的人的编号。
- 结果转换:最后的结果是从0开始计数的编号,需要加1转换为从1开始计数的编号。

示例运行
假设 n = 10,m = 3,运行上述代码会输出:
最后留下的人是原来的第 4 号
这意味着在10个人围成一圈,从1到3报数的情况下,最后留下的人是原来的第4号。约瑟夫环问题是一个经典的递归问题,可以通过多种方式解决,包括直接模拟、递归和数学公式。这里我们使用Java语言来实现一个高效的解决方案,采用数学公式的方法,因为这种方法的时间复杂度为O(n),非常高效。
约瑟夫环问题的数学解法
对于约瑟夫环问题,有一个著名的数学公式可以用来快速求解:
\[ f(n, m) = (f(n-1, m) + m) \% n \]
其中:
- \( n \) 是人数。
- \( m \) 是每次报数的数字(在这个问题中是3)。
- \( f(n, m) \) 表示从 \( n \) 个人中,每次报数到 \( m \) 的人出圈,最后剩下的人的位置(从0开始编号)。
初始条件是 \( f(1, m) = 0 \),即只有1个人时,最后剩下的人就是这个人自己,位置为0。
Java代码实现
public class JosephusProblem {
public static int findLastPerson(int n, int m) {
// 初始条件:只有1个人时,最后剩下的人的位置是0
int pos = 0;
// 动态计算每个人出圈后,最后剩下的人的位置
for (int i = 2; i <= n; i++) {
pos = (pos + m) % i;
}
// 返回最后剩下的人的位置(从1开始编号)
return pos + 1;
}
public static void main(String[] args) {
int n = 5; // 人数
int m = 3; // 报数的数字
int lastPerson = findLastPerson(n, m);
System.out.println("最后留下的是原来第 " + lastPerson + " 号的那位。");
}
}
代码解释
- findLastPerson 方法:
- 参数
n 表示人数。 - 参数
m 表示每次报数的数字。 - 初始化
pos 为0,表示只有1个人时,最后剩下的人的位置是0。 - 使用一个循环从2到n,逐步计算每轮出圈后,最后剩下的人的位置。
- 最后返回
pos + 1,因为题目要求从1开始编号。
- main 方法:
- 定义人数
n 和报数的数字 m。 - 调用
findLastPerson 方法计算最后剩下的人的位置。 - 打印结果。

示例
假设 n = 5,m = 3,则输出应该是:
最后留下的是原来第 4 号的那位。
这个方法的时间复杂度为O(n),非常高效,适用于较大的输入规模。
- 点赞
- 收藏
- 关注作者


评论(0)