约瑟夫环程序设计报告书

  • 格式:doc
  • 大小:60.59 KB
  • 文档页数:16

4:课程设计报告书附件《数据结构》课程设计报告环Joseph)约瑟夫(第七组别组组长成组员成绩教师指导计算机科学与技术系日11月6年2014.摘要约瑟夫环问题是典型的线性表的应用实例,其开发主要包括后台数据库的建立和维护以及前端应用程序的开发两个方面。

对于前者要求建立起数据一致性和完整性强、数据安全性好的库。

而对于后者则要求应用程序功能完备,易使用等特点。

经过分析,我们使用MICROSOFT公司的Microsoft Visual C++6.0开发工具,利用其提供的各种面向对象的开发工具,尤其是数据窗口这一能方便而简洁操作数据库的智能化对象,首先在短时间内建立系统原型,然后,对初始原型系统进行需求迭代,不断修正和改进,直到形成用户满意的可行系统。

关键词:单循环链表;c 语言;约瑟夫环;序言数据结构是研究数据元素之间的逻辑关系的一门课程,以及数据元素及其关系在计算机中的存储表示和对这些数据所施加的运算。

该课程设计的目的是通过课程设计的综合训练,培养分析和编程等实际动手能力,系统掌握数据结构这门课程的主要内容。

本次课程设计的内容是用单循环链表模拟约瑟夫环问题,循环链表是一种首尾相接链表,其特点是无须增加存储容量,仅对表的链接方式稍作改变,使表处理更加灵活,约瑟夫环问题就是用单循环链表处理的一个实际应用。

通过这个设计实例,了解单链表和单循环链表的相同与不同之处,进一步加深对链表结构类型及链表操作的理解。

通过该课程设计,能运用所学知识,能上机解决一些实际问题,了解并初步掌握设计、实现较大程序的完整过程,包括系统分析、编码设计、系统集成、以及调试分析,熟练掌握数据结构的选择、设计、实现以及操作方法,为进一步的应用开发打好基础。

章节安排1........................... 摘要、序言....一、问题描述1、课程设计目的.................. (4)2、课程设计任务.................. (4)二、设计过程1、设计思想(数据结构).................. . (4)2、设计表示(函数说明).................. . (5)3、详细设计(主要算法).................. . (6)4、用户手册(使用说明).................. . (6)三、测试报告1、测试用例.................. .... . (6)2、测试结果.................. .... . (6)3、分析探讨.................. .... . (7)四、总结................ .... . (10)五、附录(源程序)...... ...... ... ......... ..10六、参考文献......... .... ......... ... . (16)章节安排:一、问题描述1、课程设计目的1.掌握单向循环链表的建立。

2.掌握单向循环链表的操作。

2、课程设计任务编号是1,2,……,n的n个人按照顺时针方向围坐一圈,每个人只有一个密码(正整数)。

一开始任选一个正整数作为报数上限值m,从第一个仍开始顺时针方向自1开始顺序报数,报到m时停止报数。

报m的人出列,将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。

请设计一个程序求出出列顺序。

1.利用单向循环链表存储结构模拟此过程,按照出列的顺序输出各个人的编号。

2.输入数据:建立输入函数处理输入的数据,输入m的初值n,输入每个人的密码,建立单向循环链表。

3.输出形式:建立一个输出函数,将正确的出列顺序输出。

二、设计过程1、设计思想(数据结构)首先,设计实现瑟夫环问题的存储结构。

由于约瑟夫环本身具有循环性质,考虑采用循环链表,为了统一对表中任意节点的操作,循环链表不带头结点。

循环链表的结点定义为如下结构类型:struct Lnode /*定义链表*/{int number;int password;struct Lnode *next;}Lnode,*p,*q,*head;其次,建立一个不带头结点的循环链表并由头指针first指示。

最后,设计约瑟夫环问题的算法。

、设计表示(函数说明)2.《1》、循环链表抽象数据类型定义struct Lnode /*定义链表*/{int number;int password;struct Lnode *next;}Lnode,*p,*q,*head;《2》、本程序包含一下几个模块(1)创建单链表for(i=1;i<=n;i++) /* 建立单链表*/{if(i==1){head=p=(structLnode*)malloc(sizeof(struct Lnode));}else{q=(struct Lnode*)malloc(sizeof(struct Lnode));p->next=q;p=q;}(3) 形成循环链表p->next=head; /*形成循环链表*/() int main)主函数模块4(.3、详细设计(主要算法)4、用户手册(使用说明)正确的执行完程序后,进去程序显示屏。

首先按任意键继续,然后输入初始密码,紧接着输入测试人的数量n,其次依次输入测试人的密码,最后输入报数上限值m,完成后将进行结果的输出。

三、测试报告1、测试用例.测试数据:m的初值为20,n=7,7个人的密码依次为3,1,7,2,4,7,4,首先m=6,则正确的输出是什么?、测试结果2.3、分析与探讨无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达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 --> 0k+1 --> 1k+2 --> 2......k-3 --> n-3k-2 --> n-2序列1:0, 1, 2, 3 …n-2, n-1序列2:0, 1, 2, 3 …k-2, k, …,n-2, n-1序列3: k, k+1, k+2, k+3, …, n-2, n-1, 1, 2, 3,…,k-2,序列4:0, 1, 2, 3 …, 5, 6, 7, 8, …, n-3, n-2变换后就完完全全成为了(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]。

我们输出f[n]由于是逐级递推,不需要保存每个,程序也是异常简单:(注意编号是0 -- n-1)#include <stdio.h>int main(void){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 (The winner is %d\n, s);return 0 ;}时间复杂度为O(n),相对于模拟算法已经有了很大的提高。

算n,m等于一百万,一千万的情况不是问题了。

可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

参照上面提供的思路,我认为可以类似的得到一个更易于明白的方法,设有(1,2,3,……,k-1,k,k+1,……,n)n个数,当k出列时,那么有-->1k+1k+2 -->2......n -->n-k1 -->n-k+1......k-1 -->n-1由上面一组式子可以推出,若知道新产生的n-1个数中某个数x,那么很显然可以推出x在原数列里的位置,即x‘=(x+k)%n,由此,我们可以得到一个递推公式f[1]=1f[n]=(f[n-1]+k)%n (n>1)如果你认为上式可以推出约瑟夫环问题的解,很不幸,你错了,上面的递推公式中,在某种情况下,f[n-1]+k会整除n,如n=2,k=3,这时我们修要对上式进行修正,f[n]=(f[n-1]+k)%n;if(f[n]==0)f[n]=n;问题得解。

程序代码如下:#include<stdio.h>int main(){int n,k,s=1;scanf(%d%d,&n,&k);for(int i=2;i<=n;i+=1){s=(s+k)%i;if(s==0)s=i;}printf(ans=%d\n,s);return 0;}当然,我们还可以用递归方法解决此问题:#include<stdio.h>int main(){int jos(int n,int k);int n,k,s;scanf(%d%d,&n,&k);s=jos(n,k);printf(ans=%d\n,s);return 0;}int jos(int n,int k){int x;if(n==1)x=1;else {x=(jos(n-1,k)+k)%n;if(x==0)x=n;}return x;}四、总结我的这次数据结构课程设计的题目是:《约瑟夫环》,通过对该题目的设计,我加深了对数据结构及存储结构的理解,进一步地理解和掌握了课本中所学的各种数据结构,尤其是对单循环链表上基本运算的实现,学会了如何把学到的知识用于解决实际问题,锻炼了自己动手的能力。