求一个算法的解释(约瑟夫环)

zhaoweiting0609 2009-06-02 01:39:00
加精
大家好,我今天看到一个算法
问题的来源:
有n猴子报数,当他们报到M的时候退出队伍,然后接着报数,从1开始,问最后一猴子是谁!
有一个算法是这样的:

class Monkey
{
public int King(int M, int N)//这个算法,不理解,请高手给个解释,谢谢!
{
//总人数 M ,数到第 N 个排除。
int k=0;
for (int i = 2; i <= M; i++)
k = (k + N) % i;
return ++k;
}
static void Main(string[] args)
{
Monkey M = new Monkey();
Console.WriteLine ("第"+M.King(4,3)+"号猴子为大王。");
Console.ReadLine();
}
}
...全文
2222 78 打赏 收藏 转发到动态 举报
写回复
用AI写文章
78 条回复
切换为时间正序
请发表友善的回复…
发表回复
showcraft 2011-04-09
  • 打赏
  • 举报
回复
http://topic.csdn.net/u/20100828/09/545be4f7-2bd5-47ae-989f-e3caff6f560d.html?seed=885567882&r=68009017
huli_fox1 2011-04-09
  • 打赏
  • 举报
回复
【解决方法】
 链表方法

 非递归方法
http://baike.baidu.com/view/717633.htm

 约瑟夫环问题的数学方法
无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。
  为了讨论方便,先把问题稍微改变一下,并不影响原意:
  问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出
  ,剩下的人继续从0开始报数。求胜利者的编号。
  我们知道第一个人(编号一定是(m-1)%n) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
  k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2
  并且从k开始报0。
  现在我们把他们的编号做一下转换:
  k --> 0
  k+1 --> 1
  k+2 --> 2
  ...
  ...
  k-3 --> n-3
  k-2 --> n-2
  序列1: 1, 2, 3, 4, …, n-2, n-1, n
  序列2: 1, 2, 3, 4, … k-1, k+1, …, n-2, n-1, n
  序列3: k+1, k+2, k+3, …, n-2, n-1, n, 1, 2, 3,…, k-2, k-1
  序列4:1, 2, 3, 4, …, 5, 6, 7, 8, …, n-2, n-1
  变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:
  ∵ k=m%n;
  ∴ x' = x+k = x+ m%n ; 而 x+ m%n 可能大于n
  ∴x'= (x+ m%n)%n = (x+m)%n
  得到 x‘=(x+m)%n
  如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:
  令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n].
  递推公式:
  f[1]=0;
  f[i]=(f[i-1]+m)%i; (i>1)
  有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1由于是逐级递推,不需要保存每个f,程序也是异常简单:
#include <stdio.h>
int main()
{
int n, m, i, s = 0;
printf ("N M = ");
scanf("%d%d", &n, &m);
for (i = 2; i <= n; i++)
{
s = (s + m) % i;
}
printf ("\nThe winner is %d\n", s+1);
}
海涛 2011-04-06
  • 打赏
  • 举报
回复
8楼 犀利啊
a13945149916 2010-07-14
  • 打赏
  • 举报
回复
不算太难吧
huatuo20002008 2010-06-27
  • 打赏
  • 举报
回复
8楼的解释,豁然开朗了。牛人好多啊!
raylayre 2010-03-13
  • 打赏
  • 举报
回复
mark 学习一下
liuyangbisheng 2010-03-12
  • 打赏
  • 举报
回复
第一个为什么一定是(m%n-1),编号不是从0~n-1么?那第一个出来的编号不应该是(m-1)%(n-1)呢?
tchshnxdjk 2009-12-19
  • 打赏
  • 举报
回复
不错的帖子啊,好好研究
hehe300 2009-12-08
  • 打赏
  • 举报
回复
不错的帖子啊,非常的值得学习
over300 2009-11-26
  • 打赏
  • 举报
回复
good
pass299 2009-11-20
  • 打赏
  • 举报
回复
不错的帖子啊
j8daxue 2009-07-13
  • 打赏
  • 举报
回复
不错,其实就是写出递推公式后找规律,然后写成循环.不过不推公式的话,题目看起来很繁琐.
java1109 2009-07-05
  • 打赏
  • 举报
回复
mark
bfhtian 2009-06-29
  • 打赏
  • 举报
回复
这个算法不错,好不容易理解了
sunqiqiang 2009-06-09
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 liao05050075 的回复:]
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2
并且从k开始报0。
现在我们把他们的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
k-1 --> n-1
变换后就完完全…
[/Quote]

ls用的是Dynamic Programming(1->n), 如果用recursion(n->1)的方式:
int f(n)
{
if (n>1)
return ((f(n-1)+m))%n;
else
if (1==n)
return 0;
else
return -1;

}
陈清平 2009-06-07
  • 打赏
  • 举报
回复
不懂,帮顶。。。
changming0517 2009-06-04
  • 打赏
  • 举报
回复
好!!
banyuanhuan 2009-06-04
  • 打赏
  • 举报
回复
因为最后肯定只剩下一个了,剩下的那个就是大王
zhaoweiting0609 2009-06-04
  • 打赏
  • 举报
回复
[Quote=引用 38 楼 marsara 的回复:]
引用 25 楼 sbwwkmyd 的回复:
如果我们总是把开始数数的那个猴子的位置的位置当作起始位置,并假设x为胜出猴子的位置,如下:
第一轮淘汰中,起始位置要前进N位,相当于猴子的位置后退N位,那么胜出猴子的位置应该变为为x1=x-N;
同理,第二轮淘汰中,起始位置又要前进N位,相当于猴子的位置又后退N位,那么胜出猴子的位置应该变为为x2=x1-N;
...(注意每一轮的计算结果x?都应该取当轮总数的余数,这个应该好理解吧,因为超过猴子总数要…
[/Quote]

最后k=3意思是最后剩下的是第三号猴子
CodeProject-Jerry 2009-06-04
  • 打赏
  • 举报
回复
这个问题大学的时候做过 那个时候就是建立一个环链表, 按照要求数,数到了 就踢出环
加载更多回复(57)

33,007

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧