约瑟夫环问题
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;
一、问题的来历
据说著名犹太历史学家 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;
相关文章推荐
- 刚做的课设,有问题,不会改,求指教!
- Node.js使用MySQL的连接池
- 评测媒体猫:解决新媒体运营与营收的神器
- 【leetcode c++】80 Remove Duplicates from Sorted Array II
- lightoj 1027 A Dangerous Maze (概率)
- Android开发常见问题汇总
- 安裝 xrdp v0.6.0
- 求大神添加一个随机函数
- classroom
- [ Mod-X ]-[No.1] 第0~2关 writeup
- uva 10534 Wavio Sequence XX序列(第一个词不认识)
- 开源大数据(hadoop生态系统、流式处理系统等)处理工具汇总
- CentOS 5/6 安裝 GNOME 與 KDE 桌面環境
- 优雅的函数式编程语言
- 总结2---万用表测量方波和正弦波的电压
- polymer-quick tour of polymer
- Python mysql 基本操作
- 排列组合(求排列数)
- 如何理解面向对象
- vote