实验一约瑟夫问题.docx
- 文档编号:11701541
- 上传时间:2023-03-30
- 格式:DOCX
- 页数:18
- 大小:43.57KB
实验一约瑟夫问题.docx
《实验一约瑟夫问题.docx》由会员分享,可在线阅读,更多相关《实验一约瑟夫问题.docx(18页珍藏版)》请在冰豆网上搜索。
实验一约瑟夫问题
实验一:
约瑟夫问题求解
一、问题描述
1、实验题目:
约瑟夫(Josephus)问题的一种描述是:
编号为1,2,……,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上线值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。
2、基本要求:
试设计一个程序,按出列顺序印出个人编号。
3、测试数据:
m的初值为20;n=7,7个人的密码依次为:
3,1,7,2,4,8,4。
m的初值为6,正确的出列顺序应为:
6,1,4,7,2,3,5。
二、需求分析
1、本程序用来求出含有密码的约瑟夫问题,可以输出所有人的出列顺序。
2、程序运行后显示提示信息,提示用户输入一圈的人数n,接着输入每个人的密码,最后提示输入初始密码。
3、用户输入完毕后,程序自动输出运算结果。
三、概要设计
1、设计思路
n个人围成一圈,每个人的手中都有一个密码,这个密码决定了下一次报数的上限。
游戏规则:
①给定一个初始密码
②循环报数,报到密码值的人要出列,依次类推,直到所有的人都出列
本程序要求输入的内容:
n个人的密码及初始密码;
本程序要求输出的内容:
n个人出列的顺序。
2、数据结构
为了实现上述功能,可以采用链式存储结构。
采用链式存储结构,定义了一个存储个人信息的结构体,及两个自定义函数,分别用于创建链表和约瑟夫出列操作。
①链表抽象数据类型的定义:
#defineSLNODEstructslnode
ADTSLNODE{
数据对象:
D={
|
SLNODE,i=1,2,3....}
数据关系:
R=
}ADTSLNODE;
②自定义函数:
voidcreate_SLnode(SLNODE*p,intn)//创建队列
{创建链表,为N个人分配密码}
voidJosef(SLNODE*p,intn)//进行约瑟夫操作
{
输入初始密码m;
for()
{将出列的结点删除,并输出出列序号;}
}
③本程序的保护模块:
结构体模块
主程序模块
自定义函数模块
调用关系:
3、程序设计
主要算法的流程图:
create_SLnode()算法流程图
Josef()算法流程图
四、详细设计
1、元素类型、结点的类型及指针
#defineSLNODEstructslnode
SLNODE//每个结点的结构体
{
intnum;//num代表序号
intcode;//code代表密码
SLNODE*next;
};
2、自定义函数:
voidcreate_SLnode(SLNODE*p,intn)//创建队列,并将其尾指针指向第一个序号
{
SLNODE*r,*s;
s=p;
inti,m;
cout<<"请给这"< "< for(i=0;i { cout<<"请给第"< "< cin>>m; r=(SLNODE*)malloc(sizeof(SLNODE)); r->code=m; r->num=i+1; r->next=s->next; s->next=r; s=s->next; } p=p->next; s->next=p; } voidJosef(SLNODE*p,intn)//进行约瑟夫操作 { p=p->next; intm; inti,j; SLNODE*r; cout<<"请输入初始密码: "< cin>>m; cout<<"依次出列的序号为: "< for(i=0;i p=p->next; for(i=0;i { for(j=0;j p=p->next; cout<<(p->next)->num< m=(p->next)->code; r=p->next; p->next=r->next; } if(m%2==0) cout< else cout<<(p->next)->num< } 3、主函数: intmain() { SLNODE*p; intn; cout<<"请输入一圈的人数: "< cin>>n; p=(SLNODE*)malloc(sizeof(SLNODE)); p->next=NULL; create_SLnode(p,n); Josef(p,n); return0; } 4、函数的调用关系: 主函数main()调用自定义函数voidcreate_SLnode(SLNODE*p,intn);/*创建队列*/与voidJosef(SLNODE*p,intn);/*进行约瑟夫操作*/。 五、调试分析 1、程序中将每个人的序号及密码用结构体的形式定义,并将其存储在链表中。 2、在调试过程中遇到很多问题,比如,输出地序号有一个是重复的,经过仔细查程序,发现有一个指针所指向的空间不在循环链表内。 3、用一个类似循环链表的链表来存储结点。 为什么说是类似呢? 因为开始时申请了一个带头结点的链表,但是尾指针并不是指向头指针的,而是指向头指针所指向的结点的,相当于循环链表有一个“小尾巴”。 起初是将尾指针指向头指针的,构成了一个循环链表,但是由于头结点的存在,使出列算法不统一,因此才采用了这种类似循环链表的结构,但是,头结点就会造成所谓的内存泄露。 4、算法的时空分析: 由于采用循环链表,元素个数为n个,设有一个指向序号1的指针*p。 voidcreate_SLnode(SLNODE*p,intn);/*创建队列*/与voidJosef(SLNODE*p,intn);/*进行约瑟夫操作*/的操作的时间复杂度都是O(n)。 六、使用说明 程序运行后用户根据提示输入“一圈的人数”n,然后程序会提示用户为这n个人分配密码,输入完毕后,程序将自动给出出列的序号。 七、结果 1、2、 附录 八、附录 源程序文件清单: #include #include #defineSLNODEstructslnode SLNODE//每个结点的结构体 { intnum;//num代表序号 intcode;//code代表密码 SLNODE*next; }; voidcreate_SLnode(SLNODE*p,intn);//创建队列 voidJosef(SLNODE*p,intn);//进行约瑟夫操作 intmain() { SLNODE*p; intn; cout<<"请输入一圈的人数: "< cin>>n; p=(SLNODE*)malloc(sizeof(SLNODE)); p->next=NULL; create_SLnode(p,n); Josef(p,n); return0; } voidcreate_SLnode(SLNODE*p,intn)//创建队列,并将其尾指针指向第一个序号 { SLNODE*r,*s; s=p; inti,m; cout<<"请给这"< "< for(i=0;i { cout<<"请给第"< "< cin>>m; r=(SLNODE*)malloc(sizeof(SLNODE)); r->code=m; r->num=i+1; r->next=s->next; s->next=r; s=s->next; } p=p->next; s->next=p; } voidJosef(SLNODE*p,intn)//进行约瑟夫操作 { p=p->next; intm; inti,j; SLNODE*r; cout<<"请输入初始密码: "< cin>>m; cout<<"依次出列的序号为: "< for(i=0;i p=p->next; for(i=0;i { for(j=0;j p=p->next; cout<<(p->next)->num< m=(p->next)->code; r=p->next; p->next=r->next; } if(m%2==0) cout< else cout<<(p->next)->num< } 九、扩展——采用顺序存储结构实现 若采用顺序存储结构,利用动态申请两个数组,容量为N,分别存放密码与序号,还有两个自定义函数,分别用于创建链表和约瑟夫出列操作。 Ⅰ、概要设计 1、数据结构 ①数组的抽象数据类型的定义: ADTR[N]{ 数据对象: D={ | int,i=1,2,3....} 数据关系: R= }ADTR[N]; ②自定义函数: voidCreate_Code(inta[],intb[],intn);/*为n个人创建密码,a[]代表密码,b[]代表序号*/ {动态申请两个数组,容量为N,并为N个人分配密码} voidJosef(inta[],intb[],intn,intm);/*对这n个人进行约瑟夫操作,并输出出列序号,m代表初始密码*/ { 输入初始密码m; for() {将出列的结点删除,并输出出列序号;} } ③本程序的保护模块: 结构体模块 主程序模块 自定义函数模块 调用关系: 自定义函数模块 主函数模块 2、程序设计 主要算法的程序流程图: Create_Code()的算法流程图: Josef()的程序流程图: Ⅲ、详细设计 1、自定义函数: voidCreate_Code(inta[],intb[],intn) { inti; cout<<"请给这"< "< for(i=0;i { b[i]=i+1; cout<<"第"< "< cin>>a[i]; } } voidJosef(inta[],intb[],intn,intm) { inti,j,k; cout<<"请输入初始密码: "< cin>>m; k=(m-1)%n;//如果初始密码m>n的情况 cout<<"依次出列的序号为: "< /*算法核心,将出列的人从数组中删除, 并且只对前n-2个人用此算法*/ for(i=0;i { cout< m=a[k]; for(j=k;j { b[j]=b[j+1]; a[j]=a[j+1]; } if((k+m)%(n-i-1)==0) k=n-i-2; else k=(k+m)%(n-i-1)-1; } /*当只剩下两人时,判断密码的奇偶性,决定出列顺序即可*/ if(a[k]%2==0) { cout< cout< } else { cout< cout< } } 2、主函数 intmain() { intn; intm=20; cout<<"请输入一圈的人数: "< cin>>n; int*a=newint[n]; int*b=newint[n]; Create_Code(a,b,n); Josef(a,b,n,m); return0; } 3、函数的调用关系 主函数main()调用自定义函数voidCreate_Code(inta[],intb[],intn);/*为n个人创建密码,a[]代表密码,b[]代表序号*/与voidJosef(inta[],intb[],intn,intm);/*对这n个人进行约瑟夫操作,并输出出列序号,m代表初始密码*/。 Ⅳ、调试分析 1、程序中首先提示用户输入总人数n,然后再主函数中动态申请了两个一维数组,分别存放序号及密码,然后通过Create_Code()函数进行密码分配。 2、Josef()函数中的算法,是通过寻找规律而来,利用每出列一个人,就将该人之后的所有元素依次向前移动一格,这样可以做到算法统一。 3、在调试过程中,遇到很多问题,总是会少一个输出,经过仔细查程序,终于发现了这个Bug——追求算法统一时,却将循环的次数与标号没有对应。 4、算法的时空分析 (1)voidCreate_Code(inta[],intb[],intn)的时间复杂度为O(n),空间复杂度为O (1);voidJosef(inta[],intb[],intn,intm);的时间复杂度为O(n),空间复杂度为O(n)。 Ⅴ、使用说明 程序运行后用户根据提示输入“一圈的人数”n,然后程序会提示用户为这n个人分配密码,输入完毕后,程序将自动给出出列的序号。 Ⅵ、调试结果 (程序调试结果参见链式结构的调试结果,二者的模式完全相同。 ) Ⅶ、源程序文件清单: #include voidCreate_Code(inta[],intb[],intn);//为n个人创建密码,a[]代表密码,b[]代表序号 voidJosef(inta[],intb[],intn,intm);//对这n个人进行约瑟夫操作,并输出出列序号,m代表初始密码 intmain() { intn; intm=20; cout<<"请输入一圈的人数: "< cin>>n; int*a=newint[n]; int*b=newint[n]; Create_Code(a,b,n); Josef(a,b,n,m); return0; } voidCreate_Code(inta[],intb[],intn) { inti; cout<<"请给这"< "< for(i=0;i { b[i]=i+1; cout<<"第"< "< cin>>a[i]; } } voidJosef(inta[],intb[],intn,intm) { inti,j,k; cout<<"请输入初始密码: "< cin>>m; k=(m-1)%n;//如果初始密码m>n的情况 cout<<"依次出列的序号为: "< /*算法核心,将出列的人从数组中删除, 并且只对前n-2个人用此算法*/ for(i=0;i { cout< m=a[k]; for(j=k;j { b[j]=b[j+1]; a[j]=a[j+1]; } if((k+m)%(n-i-1)==0) k=n-i-2; else k=(k+m)%(n-i-1)-1; } /*当只剩下两人时,判断密码的奇偶性,决定出列顺序即可*/ if(a[k]%2==0) { cout< cout< } else { cout< cout< } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 约瑟夫 问题