一、约瑟夫问题
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
二、问题分析
1.基本思路
要想解决约瑟夫问题我们首先要清楚我们需要几个变量。
首先,这41个人我们需要定义一个数组来存储,
其次,我们要定一个变量来确定每个人数到几自杀,
接着,每当有一个人自杀后,数组中就会多出来一个空位,我们要把后面的人依次向前串一位,所以我们要定义一个变量来标记死亡的位置。
最后,每当一个人自杀之后,为了确定下一个自杀的位置,我们要定义一个变量来记录当前活着的人。
2代码过程分析
首先,我们选取这41个人其中的一段为例,
每个人从1开始报数,喊到三的人自杀,第一次自杀时如下图
这时,第一个人自杀,此时死亡位置为 0+自杀数-1 即 3-1=2
然后死亡位置 2 空缺,后面的人依次向前串一位。
接下来从当前位置开始,继续从1报数,到下一个喊到三的人自杀
如此进行,知道最后剩下Josephus和他的朋友停止游戏。
知道了原理我们就能很轻松写出代码
代码如下:
int people[41];
int killnumber=3;//数到几
int alive=41;//当前活着的人数
定义基本的变量。
for( int k=0;k<41 ;k++ )
{
people[k]=k+1;
}
对人数的数组进行存储。
int swwz = 0;
for ( ; alive>=1; alive--)
{
swwz = (swwz + killnumber - 1) % alive;
printf("%d ",people[swwz]);
for (int i=swwz+1 ; i<alive; i++)
{
people[i - 1] = people[i];
}
}
注意,上述代码之所以要死亡位置对人数取余是因为最后一个人报数之后要从新重第一个接着报数,取余是为了防止死亡位置超过人数。