您的位置:首页 > 其它

约瑟夫环问题

2015-07-17 22:56 204 查看
约瑟夫环问题是一个数学的应用问题:已知M个人(以编号1,2,3...M分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到N的那个人出列;他的下一个人又从1开始报数,数到N的那个人又出列;依此规律重复下去,直到圆桌周围的人只剩下一人。

一、问题的来历

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

二、递推,还是递推

  发现出题的爱出递归的大题。动态规划也是用递归思想。

  可是,目前俺还没来得及搞懂。。。感觉脑袋不够用啊。

三、问题的延展

  考官觉得直接的约瑟夫还不够搞死人,还延展了。

  N只猴子围在一圈选大王,从1开始数,数到M的猴子退出圈外,从下一只猴子继续从1开始数,数到N的猴子再退出圈外(M>N)。如果最初编号为S的猴子最后当了大王,求最小的N是多少。



四、解决思路

  这里有一个“陷阱”,N只猴子,要从0开始编号,编号到(N-1),从0开始数数,数到(M-1)的猴子退出,最后得到的结果再加1,就得到从1开始编号,从1开始数数情况下的编号。因为计算机算余数,是有0的。

  M只猴子,最后只剩一只,那么要淘汰M-1轮。

  初始编号为: 0, 1, ..., m-1, m, ..., N-1,淘汰编号m-1的猴子,从m开始重新编号0,那么,

  第1轮后编号为:N-m, N-m+1,..., 0, 1,..., N-1-m,共N-1个,可以发现,X[0]=(X[1]+m) mod N。

递推可以得到规律: X[i]=(X[i+1]+m) mod (N-i)。

  最后一轮编号必然是X[N-1]=0。那么剩下2个的时候,其编号X(N-2) =(0+m) mod 2。

  那么用一个循环来倒推,就可以得到最后剩下的猴子的原始编号了。

 int nTotleNum=0, nExit=0, nStay=0;

cout<<"请输入总共有多少只猴子:";

cin>>nTotleNum;

cout<<"请输入从1数到第几个猴子退出:";

cin>>nExit;

//N只猴子,从0开始编号,从0开始数数,数到(m-1)的猴子退出,最后得到的结果再加1,就得到从1开始编号,从1开始数数情况下的编号。

int nTmpPos=0;

for(int nPos=2;nPos<=nTotleNum;nPos++)

{

nTmpPos = (nTmpPos+nExit)%nPos;

}

nTmpPos += 1;
cout<<endl<<"最后留下原来编号是:"<<nTmpPos<<"的猴子。"<<endl;

如果是指定最后留下的猴子编号,那么用从2开始遍历,可以得到数到多少退出,最后留下指定编号的猴子。

 cout<<"请输入你打算最后让第几只猴子留下:";

cin>>nStay;

//从数到2开始

nExit = 1;

nTmpPos = 0;

do

{

nExit++;

nTmpPos=0;

for(int nPos=2; nPos<=nTotleNum; nPos++)

{

nTmpPos = (nTmpPos+nExit)%nPos;

}

nTmpPos += 1;

}while(nTmpPos!=nStay);

cout<<"至少要数到"<<nExit<<"退出,就能保留你指定的猴子。"<<endl;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: