O(N) 时间复杂度求解约瑟夫问题

问题描述

总共有 N(N ≥ 1) 个人站成一列,将他们从左向右编号为 1,2,3 ,…… ,N。编号为 1 的人从 1 开始报数,直到一个人报到 m (m ≥ 1),就将这个人移出队列,下一个人继续从 1 开始报数,直到又有人报到 m,不断重复这个过程,显然最后只会剩下一个人,请求出这个人的编号。

这就是著名的约瑟夫问题。

O(m×N) 时间复杂度的求解方式

我们首先以 N = 5,m = 3 为例对这个问题的求解进行演示。下图中编号为 3 的人首先报到 3,应该移出队列。

五
3 号被移出队列后(标记为灰色表示已移出队列),4 号继续开始从 1 报数,下一个报到 3 的就是 1号,将其移出队列。
在这里插入图片描述
然后从 2 号开始报数,因为 3 号已经被移出队列了,所以不再报数,下一个报到 3 的就是5号,将5号移出队列。
在这里插入图片描述
再从 2 号开始报数,最后 2 号报到 3,移出队列。
在这里插入图片描述
最后队列中只剩下了 4 号,计算结束。
在这里插入图片描述
以上求解过程可以用一个单向链表来模拟,这种方式较为简单,此处不做过多讨论。
在这里插入图片描述因为每将一个人移出队列,都要报 m 个数,而最终要移出 N - 1 个人,所以这种求解方式的时间复杂度为 O(m×N) 。这里给出一种 O(N) 时间复杂度求解约瑟夫问题的方法。

O(N) 时间复杂度的求解方式

我们先来考虑最简单的情况,也就是 N=1,这时无论 m 是多少,求解结果都为 1。

如果 N=2,我们假设求解结果为 F(2)。
如果 N=3,求解结果为 F(3)。
如果 N=4,求解结果为 F(4)。

最后,也就是我们要计算的最终结果就是 F(N)。

令 i=1~N,我们有必要重申一下 F(i) 的含义,这是理解该算法的关键:F(i) 指的是 i 个人玩这个游戏,他们的编号为从 1 到 i,最终仍然留在队列中的那个人的编号

显然 F(1)=1,根据递推的思想,如果我们知道 F(i-1) 和 F(i) 之间的关系,我们就能从 F(1) 开始,推导求出 F(2),F(3) 一直到 F(N) 的值。找到 F(i-1) 和 F(i) 之间的关系就是我们接下来要做的事情。

我们先计算 i 个人参与游戏的时候,报数(callNum)和编号(oldNum)之间的关系。两者之间的关系如下图所示,可以用公式表达为:

(式1) oldNum = (callNum - 1) % i + 1
在这里插入图片描述假如编号为 d 的人被移出了队列,此时队列中只剩下了 i-1 个人,我们给这个队列重新进行编号(newNum),编号方式为 d 下一个人为 1 号,之后是 2 号,3 号以此类推,如下图所示,灰色的方块表示这个人已经被移出了队列,所以新编号中没有他,oldNum 和 newNum 之间的关系可以表示为:

(式2) oldNum = (newNum + d -1) % i + 1
在这里插入图片描述因为报到了 m 的人被移出了队列,依据 (式1) 可以求得:

d = (m - 1) % i + 1

代入 (式2) 可得:

oldNum = (newNum + (m - 1) % i + 1 - 1) % i + 1

化简后为:

oldNum = (newNum + m - 1) % i + 1

至此我们就求得了新编号和旧编号之间的关系,新编号中编号为 F(i-1) 的人最终留在了队列里,那么这个人在旧编号中的编号 F(i) 即为:

F(i) = (F(i - 1) + m - 1) % i + 1

因为 F(1)=1,所以我们就可以根据上面这个公式递推得到 F(N)。这里需要明确的一点是,新编号中编号为 F(i-1) 的人和旧编号中编号为 F(i) 的人是同一个人。只不过这个人的编号发生了变化。所以递推过程可以理解为:根据新编号中存活下来的人的编号,计算出这个人在老编号中的编号。

我们还是以 N=5 和 m=3 为例,演示一下这个方法的求解步骤:

F(1) = 1
F(2) = (F(1) + 3 - 1) % 2 + 1 = 2
F(3) = (F(2) + 3 - 1) % 3 + 1 = 2
F(4) = (F(3) + 3 - 1) % 4 + 1 = 1
F(5) = (F(4) + 3 - 1) % 5 + 1 = 4

也就是说编号为 4 的那个人是最后留在队列中的人,这与我们之前的计算结果是一致的。读者可以根据下图理解一下 “新编号中编号为 F(i-1) 的人和旧编号中编号为 F(i) 的人是同一个人” 这句话。因为 F(5)=4,F(4)=1,所以下图中 oldNum 中的 4 号和 newNum中的 1 号其实是同一个人。
在这里插入图片描述
因为这种方法只需要迭代 N-1 次,所以求解的时间复杂度为 O(N)。至此我们已经完成了对 O(N) 时间复杂度下对约瑟夫问题的求解。

发布了3 篇原创文章 · 获赞 4 · 访问量 706

猜你喜欢

转载自blog.csdn.net/weixin_42466155/article/details/104312118