面试题——约瑟夫环

约瑟夫环问题的递推公式:

\[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 开始的编号

如果你还想看到递归实现非递归实现的代码,或者想扩展到多个幸存者的情况,我也可以继续为你讲解 😊

posted @ 2025-05-28 00:27  Gold_stein  阅读(93)  评论(0)    收藏  举报