面试题——约瑟夫环
约瑟夫环问题的递推公式:
\[f(1, k) = 0 \\
f(n, k) = (f(n - 1, k) + k) \% n
\]
这个公式是从递归的角度出发,通过观察子问题之间的关系推导出来的。下面我们一步步解释它的推导过程和数学原理。
🧠 一、理解问题结构
我们定义:
- $ f(n, k) $:表示当有 n 个人围成一圈,每数到 k 的人被淘汰时,最后幸存者的编号(从 0 开始)
目标是找出这个函数的表达式。
🎯 二、从简单情况入手
情况一:n = 1
只有一个人,当然他是幸存者:
\[f(1, k) = 0 \quad (\text{因为编号从 0 开始})
\]
🔁 三、从 n 到 n - 1 的递归思路
考虑有 n 个人的情况:
0, 1, 2, ..., (k-2), (k-1), k, ..., n-1
从第一个人开始报数,第 k 个被淘汰的人的编号是:
\[(k - 1) \mod n
\]
也就是编号为 k - 1 的那个人被淘汰。
这时剩下的是:
0, 1, ..., k-2, k, ..., n-1 (共 n-1 个人)
下一轮从被淘汰者的下一个位置(即编号 k % n)开始继续报数。
🔄 四、重新编号的思想
现在我们的问题变成了一个规模更小的约瑟夫环问题(n - 1 个人),但他们的编号不是连续的了。
为了简化问题,我们可以对剩下的 n - 1 个人进行重新编号,以新的起始点为 0。
例如:
- 原来编号为
k % n的人变成新编号中的 0 - 接下来依次编号:
k % n → 0,(k+1) % n → 1, ...,(k-1) % n → n-2
设在新编号体系中,最后幸存者的编号是 x,那么它在原编号体系中的编号应该是:
\[(x + k) \mod n
\]
这就是我们递推的核心思想!
📚 五、建立递推关系
我们设:
\[f(n, k):= \text{在 n 个人中,每次数到 k 的人的最终幸存者编号(从 0 开始)}
\]
如果我们知道:
\[f(n - 1, k):= \text{在 n - 1 个人中,每次数到 k 的人的最终幸存者编号}
\]
那么根据上面的重新编号分析,可以得到:
\[f(n, k) = (f(n - 1, k) + k) \mod n
\]
✅ 六、边界条件
初始条件是:
\[f(1, k) = 0
\]
因为只剩一个人,他自然是幸存者。
🧮 七、举例验证
以 n = 5, k = 2 为例:
- f(1, 2) = 0
- f(2, 2) = (0 + 2) % 2 = 0
- f(3, 2) = (0 + 2) % 3 = 2
- f(4, 2) = (2 + 2) % 4 = 0
- f(5, 2) = (0 + 2) % 5 = 2
所以编号是 2(从 0 开始),如果要从 1 开始,则加 1 得到 3。
与前面模拟结果一致 ✅
🧩 八、总结
| 步骤 | 内容 |
|---|---|
| 1. | 从简单情况入手,设定初始值 f(1, k) = 0 |
| 2. | 淘汰第一个数到 k 的人,问题变成 n - 1 的子问题 |
| 3. | 使用“重新编号”技巧,将子问题映射回原问题 |
| 4. | 推导出递推公式:f(n, k) = (f(n - 1, k) + k) % n |
| 5. | 最后加上 1 可以转换为从 1 开始的编号 |
如果你还想看到递归实现或非递归实现的代码,或者想扩展到多个幸存者的情况,我也可以继续为你讲解 😊

浙公网安备 33010602011771号