百度原始约瑟夫问题为:

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

 

约瑟夫环一般描述:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列,求最后出列者的序号。

int fun(int n, int m)
{
  int i, r = 0;
  for (i = 2; i <= n; i++)
    r = (r + m) % i;
  return r+1;
}

 》》可以这样理解这个方法:
》》当有n个人的时候,他们的编号依次是0、1、2、3、4、………、n-1。假设最后编号为x(n)的人会留下来。
》》因为数到m的那个人会出列,那么此轮中编号为(m-1)%n的人会出列,编号为(m+0)%n的人将做为下一轮编号为0的人,此轮编号为(m+i)%n的人将做为下一轮编号为i的人…
》》因此当有n-1个人的时候,编号为i的人对应着上一轮编号为(m+i)%n的人。假设此轮编号为x(n-1)的人最终会留下来。因为编号为x(n-1)的人肯定对应着上一轮的x(n),所以有x(n)=(m+x(n-1))%n
》》有了这个递推公式,那我们就可以一直递推到x(2)=(m+x(1))%2,而x(1)=0。
》》所以我们可以这么来写这个函数:
》》r = 0
》》for i 从 2 到 n:
》》》》r = (m+r)%i
》》最终第r个人会留下来(如果从1开始编号就是第r+1个人最终会留下来)。

 

求解原始约瑟夫问题:就是寻求最后2人的序号,按上面描述,也就是x(2)有两个序号0,1,其对应的x(n)的情况下序号为多少:

#include <stdio.h>
int fun0(int n,int m)
{
    int i,r=0;
    for(i=3;i<=n;i++)
    {
        r=(r+m)%i;
    }
    return r+1;
}
int fun1(int n,int m)
{
    int i,r=1;
    for(i=3;i<=n;i++)
    {
        r=(r+m)%i;
    }
    return r+1;
}

int main()
{
    int n,m,r,r2;
    scanf("%d %d",&n,&m);
    r=fun0(n,m);
    r2=fun1(n,m);
    printf("%d,%d\n",r,r2);
    return 0;
}

验证得知最后的结果为16,31,符合问题描述。