首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >约瑟夫斯问题递归实现的解释

约瑟夫斯问题递归实现的解释
EN

Stack Overflow用户
提问于 2015-08-02 19:12:46
回答 2查看 9.7K关注 0票数 25

编辑:n是人数。K是被淘汰的kth人员。因此,对于k=2,每2个人就会被淘汰。

代码语言:javascript
复制
int josephus(int n, int k)
{
 if (n == 1)
  return 1;
else
   return (josephus(n - 1, k) + k-1) % n + 1;
}

代码尽可能简单。但不知何故,我无法理解这个问题(老实说,这有点让人难堪)。

我想要理解的是,

  1. josephus(n,k)给出了n和步长k的种群的最终解。
  2. 如果我们知道约瑟夫斯(n-1,k)的解,就可以计算约瑟夫斯(n,k)。这在我看来是动态规划的“最优子结构性质”。
  3. 我得到,我们需要做一个MOD n,以便当数超过n时,它将再次从1开始计数。(即,确保加法的行为就像我们在一个圆圈中计数)。但我们为什么要加这个"k-1"?

主要问题是,如果我们知道约瑟夫斯(n-1,k)的正确解,我们如何计算约瑟夫斯(n,k)的解。我们已经有效地增加了一个人的人口,并以某种方式增加这个k-1值给了我正确的解决方案(让我们暂时忽略mod )。

有人能向我解释这一点吗?在问题的每一步,最优的子结构属性是如何保持的?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-08-02 20:22:24

对我来说,让这个解决方案有意义的关键洞察力是:josephus(n, k)的结果最好不要被认为是约瑟夫斯幸存者的数字,而是作为约瑟夫斯幸存者的数字的索引。例如,打电话给josephus(5, 2)会告诉你五个人中的一个人的索引,这个人最终幸存了下来。

考虑到这种直觉,让我们看看约瑟夫斯问题是如何工作的,通过一个具体的例子。假设我们想知道josephus(n, 2)。你可以想象我们有像这样排队的人:

代码语言:javascript
复制
1 2 3 4 5 ... n

发生的第一件事是,person 1杀死2个人,如下所示:

代码语言:javascript
复制
1 X 3 4 5 ... n

现在,我们有一个子问题,形式如下:还有n个人还活着,每个其他人都会被杀,第一个被刺伤的人是3个人。换句话说,我们剩下一个像这样形状的人环:

代码语言:javascript
复制
3 4 5 ... n 1

现在,假设我们对josephus(n - 1, 2)进行递归调用,因为我们有n-1人。这将返回谁在n-1人中幸存的指数。考虑到我们有生存的人的指数,而且我们也知道谁是开始的人,我们可以决定谁会被留下。这就是我们要做的。

这一行中的起跑者是在最后一次被处决的人之后出现的人。这将是人3。幸存者在四人圈的1索引的位置是由josephus(n - 1, 2)给出的。因此,我们可以向前走josephus(n - 1, 2) - 1的位置,如果有必要的话,绕着圆环走到最后的位置。换句话说,幸存者是按位置分配的。

代码语言:javascript
复制
 (3 + josephus(n - 1, 2) - 1) % n

不过,上面的公式有个问题。如果我们确实使用了一个索引,那么如果最后的幸存者位于n位置会发生什么呢?在这种情况下,我们会意外地返回位置0作为我们的答案,但我们真的想要位置n。作为一个修正,我们将使用一个技巧使用mod来用一个索引来环绕:我们将取里面的数量(一个索引的位置),然后减去一个得到零索引的位置。我们将这个数量乘以n,以得到零索引的位置。最后,我们将添加一个,以得到一个索引的位置,包装。看起来是这样的:

代码语言:javascript
复制
(3 + josephus(n - 1, 2) - 2) % n + 1

因此,这里的-2项来自两个独立的-1 :第一个-1是因为josephus(n - 1, 2)返回一个索引索引,所以要按正确的位置前进,我们必须采取josephus(n - 1, 2) - 1前进的步骤。第二个-1是因为我们使用的是一个索引而不是零索引。

让我们把它推广到任意k上,而不仅仅是k= 2,假设我们想知道josephus(n, k)。在这种情况下,person 1会刺伤person k,留给我们这样一个数组:

代码语言:javascript
复制
1 2 3 ... k-1 X k+1 ... n

我们现在基本上需要解决一个子问题,即person k+1优先:

代码语言:javascript
复制
k+1 k+2 ... n 1 2 ... k-1

因此,我们计算josephus(n - 1, k),得到一个n-1人环的单索引幸存者,然后通过许多步骤向前移动:

代码语言:javascript
复制
(k+1 + josephus(n - 1, k) - 1)

我们需要担心这样的情况,所以我们需要用n来表示:

代码语言:javascript
复制
(k+1 + josephus(n - 1, k) - 1) % n

然而,我们只有一个索引,所以我们需要使用从内部数量减去1,然后在末尾添加1的技巧:

代码语言:javascript
复制
(k+1 + josephus(n - 1, k) - 2) % n + 1

它简化为

代码语言:javascript
复制
(k-1 + josephus(n - 1, k)) % n + 1

这相当于

代码语言:javascript
复制
(josephus(n - 1, k) + k-1) % n + 1

就像解决方案代码一样。

概括地说: k-1项来自于从k+1位置开始,加上josephus(n - 1, k) - 1以向前移动适当的数量,然后减去一个,在末尾再加上一个以完成正确的概括。

希望这能有所帮助!

票数 37
EN

Stack Overflow用户

发布于 2021-08-18 20:12:38

我们需要调整k-1的位置,因为在kth被移除后,开始位置已经被k移动(第一个k-1被旋转到末端)。也就是说,初始位置pos变成pos。如果k= 3,(n-1,k)返回pos = 2,则原始位置应为pos +k= 5。

我们在公式中用k-1代替k,因为我们必须用mod(n):k= (k-1) %n+1。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31775604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档