Josephus问题(生死问题)简单探讨 作者:HAM 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特後,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第35个位置,于是逃过了这场死亡游戏(当39个犹太人都死了,他们两个当然就不会被逼自杀了)。 在这里我们要探讨的是有n个人围成一个圈,从头一个开始数,一个间一个的杀死(头一个不杀),直到剩下最后一个幸存者J(n),现在问题是: 先把每个人都依次从1编上号码,然后按顺序围成一个圈,问幸存的一个编号是多少? 首先我们来考虑为偶数个人时的情形: 设当前有2n个人(n为大于0的整数)编好号围成一个圈,当进行第一轮自杀时,还剩下n个人: 1,2,3,4,5,6,7,8,9,10 (开始) 1, 3, 5, 7, 9 (第一轮后) 1, 2, 3, 4, 5 (开始就只有n个人) 由上图我们可以很明显的发现2n个人进行第一轮自杀后剩下的人数的编号与没开始自杀时就有n个人时的编号关系是2m-1(m=1,2,3...n),那么2n个人中的幸存者可用递归式表示为: J(2n) = 2J(n) - 1 现在考虑奇数个人,设人数为2n+1个,那么进行第一轮后剩下n+1个,编号为: 1,2,3,4,5,6,7,8,9,10,11 (开始) 1, 3, 5, 7, 9, 11 (第一轮后) 3, 5, 7, 9, 11 (再杀掉第二轮第一个该死的) 1, 2, 3, 4, 5 (开始就只有n个人) 当杀掉第二轮第一个该死的时,就又剩下n个人了,那么此时的编号与没开始自杀时就有n个人时的编号关系是2m+1(m=1,2,3...n),那么2n+1个人中的幸存者可用递归式表示为: J(2n+1) = 2J(n) + 1 综上所述,当有n个人时,最后的幸存者为: J(1) = 1 J(n) = 2J(n/2) - 1 (n为偶数) J(n) = 2J((n-1)/2) + 1 (n为奇数) 从以上递归式,我们应该可以用二进制来分析。 有n个人,我们把n化成二进制表示: b(m),b(m-1),b(m-2),...,b(0) 比如41个人,可以化为 101001 ,这样就可以使用移位的办法来解决。 首先我们设最后幸存标号为x,并赋予0,总人数为n,用C++代码描述为: x = 0;//最后幸存者 y = 1;//辅助变量 while (n != 0) { if (n & 1 == 0) {//为偶数 x += y * -1; } else {//为奇数 x += y * 1; } n >>= 1; y <<= 1;//乘以2 } 优化为: x = n;//最后幸存者 y = 1;//辅助变量 while (n != 0) { if (n & 1 == 0) {//为偶数 x -= y; } n >>= 1; y <<= 1;//乘以2 } 程序最后运行的结果实际上就是n减去n按位取反(比如n=41=101001,取反为010110)后的值。 |