详细阐述约瑟夫环问题(报数出队问题)
2015-10-05 16:51
363 查看
约瑟夫环问题(Josephus)
用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。(约瑟夫环问题 Josephus)
直接上代码:
1 #include <stdio.h>
2 int main()
3 {
4 int n, m, i, s = 0;
5 printf ("N M = ");
6 scanf("%d%d", &n, &m);
7 for (i = 2; i <= n; i++)
8 {
9 s = (s + m) % i;
10 }
11 printf ("\nThe winner is %d\n", s+1);
12 }
刚接触这类问题的同学是不是有一种“我靠,为什么这么少”的感觉,然而只要这么少。
接下来讲解一下代码,既然作为一个经典的问题,那么此算法也当然不仅仅是对游戏过程的模拟了,更是对数学本质的一个分析。
为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述: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。
现在我们把n-1个人的时候的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
变换后就完完全全成为了(n-1)个人报数的子问题!(这便不是让我们倒推吗?)
假如我们知道这个子问题的解:例如A[n-1]是在n-1个人中的最终胜利者,那么根据上面对A[n-1]进行转换
便刚好就是n个人情况的解。变回去的公式很简单,如下:
A
=(A[n-1] +k)%n。(k是在n个人中第一个出列的人的编号,此处这个公式涉及了上面叙述到的编号的转换,需要搞懂了才能理解)
由于k=m%n,那么公式也可以写为A
=(A[n-1]
+m)%n。为什么呢?很容易理解,7%3=1 , 1%3=1。
如此也是得出了递推公式:
A[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是A
A[1]=0;
A[2]=(A[1]+m)%2;
A[i]=(A[i-1]+m)%i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出A[i]的数值,最后结果是A
。因为实际生活中编号总是从1开始,我们输出A
+1
由于是逐级递推,不需要保存每个A[i],于是便得到了最终的程序:
1 #include <stdio.h>
2 int main()
3 {
4 int n, m, i, s = 0;
5 printf ("N M = ");
6 scanf("%d%d", &n, &m);
7 for (i = 2; i <= n; i++)
8 {
9 s = (s + m) % i;
10 }
11 printf ("\nThe winner is %d\n", s+1);
12 }
参考博客:http://blog.csdn.net/ZCSYLJ/article/details/6830703
本人博客,算法均为新手,闻过则喜,望各前辈不吝指点批评。
用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。(约瑟夫环问题 Josephus)
直接上代码:
1 #include <stdio.h>
2 int main()
3 {
4 int n, m, i, s = 0;
5 printf ("N M = ");
6 scanf("%d%d", &n, &m);
7 for (i = 2; i <= n; i++)
8 {
9 s = (s + m) % i;
10 }
11 printf ("\nThe winner is %d\n", s+1);
12 }
刚接触这类问题的同学是不是有一种“我靠,为什么这么少”的感觉,然而只要这么少。
接下来讲解一下代码,既然作为一个经典的问题,那么此算法也当然不仅仅是对游戏过程的模拟了,更是对数学本质的一个分析。
为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述: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。
现在我们把n-1个人的时候的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
变换后就完完全全成为了(n-1)个人报数的子问题!(这便不是让我们倒推吗?)
假如我们知道这个子问题的解:例如A[n-1]是在n-1个人中的最终胜利者,那么根据上面对A[n-1]进行转换
便刚好就是n个人情况的解。变回去的公式很简单,如下:
A
=(A[n-1] +k)%n。(k是在n个人中第一个出列的人的编号,此处这个公式涉及了上面叙述到的编号的转换,需要搞懂了才能理解)
由于k=m%n,那么公式也可以写为A
=(A[n-1]
+m)%n。为什么呢?很容易理解,7%3=1 , 1%3=1。
如此也是得出了递推公式:
A[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是A
A[1]=0;
A[2]=(A[1]+m)%2;
A[i]=(A[i-1]+m)%i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出A[i]的数值,最后结果是A
。因为实际生活中编号总是从1开始,我们输出A
+1
由于是逐级递推,不需要保存每个A[i],于是便得到了最终的程序:
1 #include <stdio.h>
2 int main()
3 {
4 int n, m, i, s = 0;
5 printf ("N M = ");
6 scanf("%d%d", &n, &m);
7 for (i = 2; i <= n; i++)
8 {
9 s = (s + m) % i;
10 }
11 printf ("\nThe winner is %d\n", s+1);
12 }
参考博客:http://blog.csdn.net/ZCSYLJ/article/details/6830703
本人博客,算法均为新手,闻过则喜,望各前辈不吝指点批评。
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#约瑟夫问题解决方法
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析