约瑟夫问题

问题描述

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,\(39\) 个犹太人与Josephus及他的朋友躲到一个洞中,\(39\)个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,\(41\)个人排成一个圆圈,由第\(1\)个人开始报数,每报数到第\(3\)人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过\(k-2\)个人(因为第一个人已经被越过),并杀掉第\(k\)个人。接着,再越过\(k-1\)个人,并杀掉第\(k\)个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第\(16\)个与第\(31\)个位置,于是逃过了这场死亡游戏。[1]

简单描述:\(0,1,\cdots,n-1\)\(n\)个数字排成一个圆圈,从数字\(0\)开始,每次从这个圆圈里删除第\(m\)个数字,并将起点标记为第\(m+1\)个数字,求出这个圆圈里剩下的最后一个数字。

例如:

输入:n = 5, m = 3

对应数字为:0 1 2 3 4,删除的前4个数字依次为:2 0 4 1,最终剩下的数字为3。

解决方法

问题1\(0,1,\cdots,n-1\),删除第\(m\)个数字(第\(m\)个数字的序号为\((m-1)\))后,其余数字的序号是如何改变的?

若原序列某一位的序号为\(k\),删除第\(m\)个数字后,该数字的序号变为\(k'\)

\(k>m-1\)(该数字在被删除数字之后),则\(k'=k-m\)

\(k<m-1\)(该数字在被删除数字之前),则\(k'=n-m+k\)

综合考虑,有:\(k'=(k-m)\%n\)\(\%\)表示求余。

问题2:已知删除第\(m\)个数字后,某数字的序号为\(k'\),此序列中剩余数字的个数为\(n-1\),则删除第\(m\)个数字前,该数字的序号是?

根据问题1,我们已经得到了由\(k\)\(k'\)的关系式,那么由\(k'\)\(k\)的关系式可如下计算:

因为:

\[k'=(k-m)\%n \]

则:

\[k-m=\tau \cdot n + k' \ (\tau \in Z) \]

有:

\[k=\tau \cdot n + k + m \]

又已知:\(k \in [0, n-1]\)
则:

\[k=(\tau \cdot n + k + m)\%n=(k + m)\%n \]

从而我们推出了由\(k'\)\(k\)的关系式:

\[k=(k + m)\%n \]

问题3:说这么多废话,约瑟夫问题到底怎么解决?

对于最后的胜利者,也就是最终剩下的那个数字,他在这一轮的序号一定是\(0\)

根据问题2中的阐述,我们可以推导出:这个胜利数字,在倒数第二轮中,他的序号是多少。(\((0+m)\% 2\)

依次类推,得到如下递关系式:

\[\left\{ \begin{array}{**lr**} F(n) = (F(n-1) + m) \% n \\ F(1) = 0 \end{array} \right. \]

代码实现

具体题目描述见 LeetCode 剑指 Offer 62

class Solution {
public:
    int lastRemaining(int n, int m) {
        int count = 1;
        int prev = 0;
        int curr;
        while(count <= n) {
            curr = (prev + m) % count;
            prev = curr;
            count++;
        }
        return curr;
    }
};

  1. Ronald L.Graham,Donald E.Knuth,Oren Patashnik .具体数学计算机基础(第2版) :人民邮电出版社,2013:7 ↩︎

posted @ 2020-09-16 21:19  行者橙子  阅读(343)  评论(0编辑  收藏  举报