约瑟夫问题
问题描述
据说著名犹太历史学家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 \in [0, n-1]\)
则:
从而我们推出了由\(k'\)求\(k\)的关系式:
问题3:说这么多废话,约瑟夫问题到底怎么解决?
对于最后的胜利者,也就是最终剩下的那个数字,他在这一轮的序号一定是\(0\)。
根据问题2中的阐述,我们可以推导出:这个胜利数字,在倒数第二轮中,他的序号是多少。(\((0+m)\% 2\))
依次类推,得到如下递关系式:
代码实现
具体题目描述见 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;
}
};
Ronald L.Graham,Donald E.Knuth,Oren Patashnik .具体数学计算机基础(第2版) :人民邮电出版社,2013:7 ↩︎