约瑟夫问题求解
约瑟夫问题求解
约瑟夫问题求解一、问题描述。
1、实验题目约瑟夫(Joseph)问题的一种描述是:编号为1,2,..., n 的n 个人按顺时针方向围坐一圈, 每人持有一个密码(正整数)。
一开始选任一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将它的密码作为新的m值,再从下个人开始新一轮报数,如此反复,直到剩下最后一人则为获胜者。
试设计一个程序求出出列顺序。
2、实验要求利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
3、测试数据n=7,7 个人的密码依次为:3,1,7,2,4,8,4 。
m的初值为20,则正确的出列顺序应为6,1,4,7,2,3,5。
4、输入输出输入数据:建立输入处理输入数据,输入n输入以及每个人的密码;m的初值。
输出形式:建立一个输出函数,输出正确的序列。
二、需求分析1、本程序实现求解约瑟夫问题,2、程序运行后,要求用户指定初始报数上限值,然后读取个人密码。
输入数据:建立输入处理输入数据,输入m的初值,n,输入每个人的密码,建立单循环链表。
输出形式:建立一个输出函数,将正确的序列输出。
三、概要设计定义的抽象数据类型:栈的抽象数据类型定义:ADT Stack{数据对象:D={ai|ai∈ElemSet,i=1,2,...,n, n≥0,ElemSet为元素集合}数据关系:R={<ai-1,ai>|ai-1,ai ∈D,i=1,2,...,n}基本操作:InitStack(&S); //构造空栈DestroyStack(&S); //销毁栈ClearStack(&S); //将栈置空StackEmpty(S); //检查栈是否为空StackLength(S); //返回栈中元素个数GetTop(S,&e); //返回栈顶元素赋予ePush(S,e); //插入e为新的栈顶元素Pop(S,&e); //删除栈顶元素并赋予e StackTravers(S); //依次输出栈中元素。
实验报告 约瑟夫问题
pCur->next = pNew;
pCur = pNew;
printf("结点%d,密码%d\n",pCur->id, pCur->cipher);
}
}
printf("完成单向循环链表的创建!\n");
}
(3)运行"约瑟夫环"问题
static void StartJoseph(NodeType **, int)
exit(-1);
}
pNew->id = iId;
pNew->cipher = iCipher;
pNew->next = NULL;
return pNew;
}
(6)测试链表是否为空,空为TRUE,非空为FALSE
static unsigned EmptyList(const NodeType *pHead)
实验内容
利用循环链表实现约瑟夫环求解。
实验说明
1.问题描述
约瑟夫问题的:编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数。报M的人出列,将他的密码作为新的M值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
{
if(!pHead)
{
return TRUE;
}
return FALSE;
}
实验中遇到的问题及解决方法
实验结果如下:
实验总结(结果和心得体会)
实验一、约瑟夫问题
实验一:约瑟夫问题求解一、问题描述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、数据结构为了实现上述功能,可以采用链式存储结构。
采用链式存储结构,定义了一个存储个人信息的结构体,及两个自定义函数,分别用于创建链表和约瑟夫出列操作。
①链表抽象数据类型的定义: #define SLNODE struct slnodeADT SLNODE{数据对象:D={ i a |i a ∈SLNODE, i=1,2,3.... }数据关系:R=φ}ADT SLNODE;②自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列{ 创建链表,为N 个人分配密码 }void Josef(SLNODE *p,int n)//进行约瑟夫操作{输入初始密码m;for(){ 将出列的结点删除,并输出出列序号;}}③本程序的保护模块:结构体模块主程序模块自定义函数模块调用关系:3、程序设计主要算法的流程图:create_SLnode( )算法流程图Josef( )算法流程图四、详细设计1、元素类型、结点的类型及指针#define SLNODE struct slnodeSLNODE//每个结点的结构体{int num;//num代表序号int code;//code代表密码SLNODE *next;};2、自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列,并将其尾指针指向第一个序号{SLNODE *r,*s;s=p;int i,m;cout<<"请给这"<<n<<"个人分配密码:"<<endl;for(i=0;i<n;i++){cout<<"请给第"<<i+1<<"个人输入密码:"<<endl;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;}void Josef(SLNODE *p,int n)//进行约瑟夫操作{p=p->next;int m;int i,j;SLNODE *r;cout<<"请输入初始密码:"<<endl;cin>>m;cout<<"依次出列的序号为:"<<endl;for(i=0;i<n-1;i++)p=p->next;for(i=0;i<n-2;i++){for(j=0;j<m-1;j++)p=p->next;cout<<(p->next)->num<<endl;m=(p->next)->code;r=p->next;p->next=r->next;}if(m%2==0)cout<<p->num<<endl<<(p->next)->num<<endl;elsecout<<(p->next)->num<<endl<<p->num<<endl;}3、主函数:int main(){SLNODE *p;int n;cout<<"请输入一圈的人数:"<<endl;cin>>n;p=(SLNODE *)malloc(sizeof(SLNODE));p->next=NULL;create_SLnode(p,n);Josef(p,n);return 0;}4、函数的调用关系:主函数main()调用自定义函数void create_SLnode(SLNODE *p,int n);/*创建队列*/与void Josef(SLNODE *p,int n);/*进行约瑟夫操作*/。
约瑟夫问题
4、狐狸捉兔子
题目:
围绕着山顶有10个洞,狐狸要吃兔子,兔子说:“可以,但必须找到我,我就藏身于这十个洞中,你从10号洞出发,先到1号洞找,第二次隔1个洞找,第三次隔2个洞找,以后如此类推,次数不限。”但狐狸从早到晚进进出出了1000次,仍没有找到兔子。问兔子究竟藏在哪个洞里?
参考程序下载
--------------------------------------------------------------------------------
5、进制转换
题目:
将一个十进制自然数转换成二进制数,一般采取除2取余法。从键盘输入一个十进制自然数(约定该数小于等于Maxlongint),输出相应的二进制数
设有一天平,可用来称物体的质量,同时给出一个正整数n(n<=10)。
问题1:试设计n个砝码的质量,用它们能称出尽可能多的1,2,3,...连续整数质量,约定砝码可以放在天平的左右两个托盘中的任何一个或两个中。例如n=2,此时设计2个砝码的质量分别为1,3,则能同时称取1,2,3,4。
问题2:在给出n个砝码能称出最大质量范围内的一个质量x,试给出称取x的方案。如上例中:
6/7,7/8,1/1
编程求出n级法雷序列,每行输出10个分数。n的值从键盘输入。
--------------------------------------------------------------------------------
13、砝码设计
题目:
①把组成这个四位数的4个数字由小到大排列,形成由这四个数字组成的最大的四位数;
②把组成这个四位数的4个数字由大到小排列,形成由这四个数字组成的最小的四位数(如果含有数字0,则不足四位);
信息学竞赛3 - 2约瑟夫问题
约瑟夫问题
著名犹太历史学家 Josephus有过以下的故事:
在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他 的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌 人抓到,于是决定了一个自杀方式,41个人排成一个圆圈 ,由第1个人开始报数,每报数到第3人该人就必须自杀, 然后再由下一个重新报数,直到所有人都自杀身亡为止。
Page
6
约瑟夫问题第1种解法
设一个包括m个元素的数组,
初始值将数组的每个元素放1。 从第一个元素开始,依次取数组元素相加,当其和为 n时,输出该元素的下标(它即是应该出圈人的编号 ),然后将该元素清0。使以后相加时不再起作用, 相当于该人已出圈。 再从下一个元素开始,依次取数组元素相加,当其和 为n时,再输出该元素的下标。
设一个包括m个元素的数组,在每个数组元素中存放 与其相连的下一个元素的编号。 当某人出圈时,将对应元素的值放入前一元素中,使 得前一元素跳过该元素与再下一元素相连。
Page
9
数组构成的环
A1 2
A2 3
A3 4
A4 5
A5 1
4 3 5
A2
A1
2
Page 10
1
数组构成的环
A1 2
A2 3
Page
4
狼追兔子
一只兔子躲进了n个环形分布的洞的某一个中。
狼在第一个洞没有找到兔子,就隔一个洞,到第三个 洞去找;也没有找到,就隔两个洞,到第六个洞去找 。 以后每次多一个洞去找兔子……,这样下去,如果一 直找不到兔子,请问兔子可能在哪个洞中?
Page
5
猴子选大王
一堆猴子都有编号,编号是1,2,3 ...m,这群猴子 (m个)按照1-m的顺序围坐一圈,从第1开始数,每 数到第N个,该猴子就要离开此圈,这样依次下来, 直到圈中只剩下最后一只猴子,则该猴子为大王。
约瑟夫问题大全
“约瑟夫”问题及若干变种林厚从例1、约瑟夫问题(Josephus)[问题描述]M只猴子要选大王,选举办法如下:所有猴子按1…M编号围坐一圈,从第1号开始按顺序1,2,…,N报数,凡报到N的猴子退出到圈外,再从下一个猴子开始继续1~ N报数,如此循环,直到圈内只剩下一只猴子时,这只猴子就是大王。
M和N由键盘输入,1≤N,M≤10000,打印出最后剩下的那只猴子的编号。
例如,输入8 3,输出:7。
[问题分析1]这个例题是由古罗马著名史学家Josephus提出的问题演变而来的,所以通常称为Josephus(约瑟夫)问题。
在确定程序设计方法之前首先来考虑如何组织数据,由于要记录m只猴子的状态,可利用含m 个元素的数组monkey来实现。
利用元素下标代表猴子的编号,元素的值表示猴子的状态,用monkey[k]=1表示第k只猴子仍在圈中,monkey[k]=0则表示第k只猴子已经出圈。
程序采用模拟选举过程的方法,设变量count表示计数器,开始报数前将count置为0,设变量current表示当前报数的猴子编号,初始时也置为0,设变量out记录出圈猴子数,初始时也置为0。
每次报数都把monkey[current]的值加到count上,这样做的好处是直接避开了已出圈的猴子(因为它们对应的monkey[current]值为0),当count=n时,就对当前报数的猴子作出圈处理,即:monkey[current]:=0,count:=0,out:=out+1。
然后继续往下报数,直到圈中只剩一只猴子为止(即out=m-1)。
参考程序如下:program josephus1a {模拟法,用数组下标表示猴子的编号}const maxm=10000;var m,n,count,current,out,i:integer;monkey:array [1..maxm] of integer;beginwrite('Input m,n:');readln(m,n);for i:=1 to m do monkey[i]:=1;out:=0; count:=0; current:=0;while out<m-1 dobeginwhile count<n dobeginif current<m then current:=current+1 else current:=1;count:=count+monkey[current];end;monkey[current]:=0; out:=out+1; count:=0end;for i:=1 to m doif monkey[i]=1 then writeln('The monkey king is no.',i);readlnend.[运行结果]下划线表示输入Input m,n:8 3The monkey king is no.7 {时间:0秒}Input m,n:10000 1987The monkey king is no.8544 {时间:3秒}[反思]时间复杂度很大O(M*N),对于极限数据会超时。
数据结构实验报告—约瑟夫问题求解
《计算机软件技术基础》实验报告I—数据结构实验一、约瑟夫斯问题求解一、问题描述1.实验题目:编号1,2,....,n的n个人顺时针围坐一圈,每人持有一个密码(正整数)。
开始选择一个正整数作为报数上限m,从第一个人开始顺时针自1报数,报到m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,直至所有人全部出列。
2.基本要求:利用单向循环链表存储结构模拟此过程,按照出列的顺序印出个人的编号。
3.测试数据:n=7,7个人的密码依次为:3,1,7,2,4,8,4.m初值为6(正确的出列顺序应为6,1,4,77,2,3)。
二、需求分析1.本程序所能达到的基本可能:该程序基于循环链表来解决约瑟夫问题。
用循环链表来模拟n个人围坐一圈,用链表中的每一个结点代表一个人和他所代表的密码。
在输入初始密码后m,对该链表进行遍历,直到第m个结点,令该结点的密码值作为新的密码值,后删除该结点。
重复上述过程,直至所有的结点被释放空间出列。
2.输入输出形式及输入值范围:程序运行后提示用户输入总人数。
输入人数n后,程序显示提示信息,提示用户输入第i个人的密码,在输入达到预定次数后自动跳出该循环。
程序显示提示信息,提示用户输入初始密码,密码须为正整数且不大于总人数。
3.输出形式提示用户输入初始密码,程序执行结束后会输出相应的出列结点的顺序,亦即其编号。
用户输入完毕后,程序自动运行输出运行结果。
4.测试数据要求:测试数据n=7,7个人的密码依次为:3,1,7,2,4,8,4。
m初值为6(正确的出列顺序应为6,1,4,7,2,3,5)。
三、概要设计为了实现上述功能,应用循环链表来模拟该过程,用结构体来存放其相应的编号和密码信息,因此需要循环链表结构体这个抽象数据类型。
1.循环链表结构体抽象数据类型定义:ADT Node{数据对象:D={ai,bi,ci|ai∈int, bi∈int,ci∈(Node*),i =1,2...,n,n ≥0}:数据关系:R=∅基本操作:CreatList(int n) //构建循环单链表;Order(int m,node *l) //输出函数,输出出列顺序并删除链表中的结点;}ADT node;2. ADT的C语言形式说明:typedef struct Node{int num; //结点的数据域,存放编号;int word; //结点的数据域,存放密码;struct Node *next; //结点的指针域,存放指向下一结点的指针;}Node;Node *CreatList( ) //建立循环单项链表;void Order(Node *h) //输出出列顺序并删除结点;3. 主程序流程及其模块调用关系:1).主程序流程:先提示用户输入相关数据:总人数,运行循环链表结构体模块,输入每个人持有的密码值,创建出新链表,运行输出函数模块,再输入初始密码m值,输出出列序列。
约 瑟 夫 环 问 题 的 三 种 解 法
Java 约瑟夫环问题的两种解法(循环数组,单向环形链表)Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1=k=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
2.解决方法一:循环数组提示:每次报数,如果满足出圈的条件就将数组元素设置为-1,当下次报数时跳过-1。
直至数组最后一个元素变为-1,循环结束,数组的循环使用取模来完成。
--解决方法二:数组+取模public static void JosephuByArr(int total,int startNum,int m){ int []Arr=new int[total];int leave=total; --剩下的数量int count=0; --报数(0-1-2-3-4-5····)int index=startNum; --第一个元素--初始化数组(为了方便取模,最后一个元素放在数组的第0个,也可以先加一再取模)Arr[0]=total;for(int i=1;iArr.length;i++){Arr[i]=i;while(leave0){count++; --报数--找到报数为count的数组元素if(Arr[index%total]==-1){while(Arr[index%total]==-1){index++;--如果满足条件,输出(元素设置为-1)if(count%m==0){System.out.print(Arr[index%total]+"t");Arr[index%total]=-1;leave--;--下一个元素开始index++;3.解决方法二:单向环形链表提示:用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
约瑟夫环问题的两种解法(详解)
约瑟夫环问题的两种解法(详解)约瑟夫环问题的两种解法(详解)题⽬:Josephus有过的故事:39 个犹太⼈与Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被敌⼈抓。
于是决定了⾃杀⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀。
然后下⼀个重新报数,直到所有⼈都⾃杀⾝亡为⽌。
然⽽Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在第16个与第31个位置,于是逃过了这场死亡游戏。
对于这个题⽬⼤概两种解法:⼀、使⽤循环链表模拟全过程⼆、公式法我们假设这41个⼈编号是从0开始,从1开始报数,第3个⼈⾃杀。
1、最开始我们有这么多⼈:[ 0 1 2 3 4 5 ... 37 38 39 40 ]2、第⼀次⾃杀,则是(3-1)%41=2 这个⼈⾃杀,则剩下:[ 0 1 3 4 5 ... 37 38 39 40 ]3、然后就是从编号为3%41=3的⼈开始从1报数,那么3号就相当于头,既然是头为什么不把它置为0,这样从它开始就⼜是与第1,2步⼀样的步骤了,只是⼈数少了⼀个,这样不就是递归了就可以得到递归公式。
想法有了就开始做:4、把第2步中剩下的⼈编号减去3映射为:[ -3 -2 0 1 2 ... 34 35 36 37 ]5、出现负数了,这样不利于我们计算,既然是环形,37后⾯报数的应该是-3,-2,那么把他们加上⼀个总数(相当于加上360度,得到的还是它)[ 38 39 0 1 2 3 ... 34 35 36 37 ]6、这样就是⼀个总数为40个⼈,报数到3杀⼀个⼈的游戏。
这次⾃杀的是第5步中的(3-1)%40=2号,但是我们想要的是第2步中的编号(也就是最初的编号)那最初的是多少?对应回去是5;这个5是如何得到的呢?是(2+3)%41得到的。
⼤家可以把第5步中所有元素对应到第2步都是正确的。
7、接下来是[ 35 36 37 38 0 1 2... 31 32 33 34 ]⾃杀的是(3-1)%39=2,先对应到第5步中是(2+3)%40=5,对应到第2步是(5+3)%41=8。
约瑟夫问题多种解决方法
著名约瑟夫问题一
• 17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了 这样一个故事:15个教徒和15 个非教徒在深海上遇险, 必须将一半的人投入海中,其余的人才能幸免于难,于是 想了一个办法:30个人围成一圆圈,从第一个人开始依次 报数,每数到第九个人就将他扔入大海,如此循环进行直 到仅余15个人为止。问怎样排法,才能使每次投入大海的 都是非教徒。题目中30个人围成一圈,因而启发我们用一 个循环的链来表示。可以使用结构数组来构成一个循环链。 结构中有两个成员,其一为指向下一个人的指针,以构成 环形的链;其二为该人是否被扔下海的标记,为1表示还 在船上。从第一个人开始对还未扔下海的人进行计数,每 数到9时,将结构中的标记改为0,表示该人已被扔下海了。 这样循环计数直到有15个人被扔下海为止
约瑟夫问题的另外一个有名的例子
• 一堆猴子都有编号,编号是1,2,3 ...m , 这群猴子(m个)按照1-m的顺序围坐一圈, 从第1开始数,每数到第N个,该猴子就要 离开此圈,这样依次下来,直到圈中只剩 下最后一只猴子,则该猴子为大王。
• 二. 基本要求: (1) 输入数据:输入 m,n m,n 为整数,n<m (2)中文提示 按照m个猴子,数n 个数的方法,输出为大 王的猴子是几号 ,建立一个函数来实现此 功能
12
• p2->next=p1; • p2=p1; •} • p2->next=head; • return head; •} • struct monkey *findout(struct monkey *start,int
n) •{ • int i; • struct monkey *p; • i=n; • p=start;
• cout<<"----------------------------------------
实验一约瑟夫问题求解
实验一约瑟夫问题求解1)内容:约瑟夫(Joseph)问题的一种描述是:编号为1,2,..., n 的n 个人按顺时针方向围坐一圈, 每人持有一个密码(正整数)。
一开始选任一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将它的密码作为新的m值,再从下个人开始新一轮报数,如此反复,直到剩下最后一人则为获胜者。
试设计一个程序求出出列顺序。
2)要求:利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
3)测试数据:n=7,7 个人的密码依次为:3,1,7,2,4,8,4 。
m的初值为20,则正确的出列顺序应为6,1,4,7,2,3,5。
4)输入输出:输入数据:建立输入处理输入数据,输入n输入以及每个人的密码;m的初值。
输出形式:建立一个输出函数,输出正确的序列。
实验二停车场问题1)内容:设停车场是一个可停放 n 辆汽车的狭长通道,且只有一个大门可供汽车进出。
汽车在停车场内按车辆到达时间的先后顺序,依次由北向南排列(大门在最南端,最先到达的在最北端),若停车场内已经停满 n辆车,那么后来的车只能在场外等候,一旦有车开走,则等候在第一位的车即可开入(这是一个队列设长度为m);当停车场内某辆车需要开出,则在它之后的车辆必须给它让道,当这辆车驶出停车场后,其他车辆按序入栈。
每辆车按时间收费。
2)要求:以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入数据的序列进行模拟管理。
每一组输入数据包括三个数据:汽车的“到达”(’A’表示)或“离去”(’D’表示)信息,汽车标识(牌照号)以及到达或离去的时刻。
对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或者便道上的停车位置;若是车辆离去,则输出汽车在停车场停留的时间和应缴纳的费用(便道上不收费)。
栈以顺序结构实现,队列以链表结构实现。
3)测试数据:设 n=3,m=4,停车价格为p=2。
约瑟夫环问题(最简单的数学解法)
约瑟夫环问题(最简单的数学解法)基本问题描述:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。
从编号为1的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列。
(也类似于变态杀⼈狂问题)通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。
通常,我们会要求输出最后⼀位出列的⼈的序号。
那么这⾥主要研究的是最后⼀个出列的⼈的序号要怎么确定。
当n,m数据量很⼩的时候,我们可以⽤循环链表模拟约瑟夫环的过程。
当模拟到⼈数等于1的时候,输出剩下的⼈的序号即可。
这种⽅法往往实现起来⽐较简单,⽽且也很容易理解。
但是时间复杂度却是很糟糕的,达到了O(n m),这样的话,其实在n,m⽐较⼤的时候(n m达到10^8或者更⼤),那么要得出结果往往需要耗费很长的时间,但是我们可以运⽤⼀点数学上的技巧,将最后结果推导出来。
为了简化出列的过程:⾸先我们把这n个⼈的序号编号从0~n-1(理由很简单,由于m是可能⼤于n的,⽽当m⼤于等于n时,那么第⼀个出列的⼈编号是m%n,⽽m%n是可能等于0的,这样编号的话能够简化后续出列的过程),当数到m-1的那个⼈出列,因此我们编号完成之后,开始分析出列的过程:第⼀次出列:⼀开始的时候,所有⼈的编号排成序列的模式即为:0,1,2,3,4,5...n-2,n-1那么第⼀次出列的⼈的编号则是(m-1)%n1,那么在第⼀个⼈出列之后,从他的下⼀个⼈⼜开始从0开始报数,为了⽅便我们设k1 =m%n1(n1为当前序列的总⼈数)那么在第⼀个⼈出列之后,k1则是下⼀次新的编号序列的⾸位元素,那么我们得到的新的编号序列为:k1,k1+1,k1+2,k1+3...n-2,n-1,0,1,2...k1-3,k1-2 (k1-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从0开始报数,那么在这个新的序列中,每个⼈报的相应数字为:0,1,2,3....n-2那么第⼆次每个⼈报的相应数字与第⼀次时⾃⼰相应的编号对应起来的关系则为:0 --> k11 --> k1+12 --> k1+2...n-2 ---> (k1+n-2)%n1(n1为当前序列的总⼈数,因为是循环的序列,k1+n-1可能⼤于总⼈数)那么这时我们要解决的问题就是n-1个⼈的报数问题(即n-1阶约瑟夫环的问题)可能以上过程你还是觉得不太清晰,那么我们重复以上过程,继续推导剩余的n-1个⼈的约瑟夫环的问题:那么在这剩下的n-1个⼈中,我们也可以为了⽅便,将这n-1个⼈编号为:0,1,2,3,4...n-2那么此时出列的⼈的编号则是(m-1) % n2(n2为当前序列的总⼈数),同样的我们设k2 = m % n2,那么在这个⼈出列了以后,序列重排,重排后新的编号序列为:k2,k2+1,k2+2,k2+3...n-2,n-1,0,1,2...k2-3,k2-2 (k2-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从1开始报数,那么在这个新的序列中,每个⼈报的相应数字为:1,2,3,4....n-2那么这样的话是不是⼜把问题转化成了n-2阶约瑟夫环的问题呢?后⾯的过程与前两次的过程⼀模⼀样,那么递归处理下去,直到最后只剩下⼀个⼈的时候,便可以直接得出结果当我们得到⼀个⼈的时候(即⼀阶约瑟夫环问题)的结果,那么我们是否能通过⼀阶约瑟夫环问题的结果,推导出⼆阶约瑟夫环的结果呢?借助上⾯的分析过程,我们知道,当在解决n阶约瑟夫环问题时,序号为k1的⼈出列后,剩下的n-1个⼈⼜重新组成了⼀个n-1阶的约瑟夫环,那么假如得到了这个n-1阶约瑟夫环问题的结果为ans(即最后⼀个出列的⼈编号为ans),那么我们通过上述分析过程,可以知道,n阶约瑟夫环的结果(ans + k)%n(n为当前序列的总⼈数),⽽k = m%n则有:n阶约瑟夫环的结果(ans + m % n)%n,那么我们还可以将该式进⾏⼀下简单的化简:当m<n时,易得上式可化简为:(ans + m)% n⽽当m>=n时,那么上式则化简为:(ans % n + m%n%n)% n即为:(ans % n + m%n)% n⽽(ans + m)% n = (ans % n + m%n)% n因此得证(ans + m % n)%n = (ans + m)% n这样的话,我们就得到了递推公式,由于编号是从0开始的,那么我们可以令f[1] = 0; //当⼀个⼈的时候,出队⼈员编号为0f[n] = (f[n-1] + m)%n //m表⽰每次数到该数的⼈出列,n表⽰当前序列的总⼈数⽽我们只需要得到第n次出列的结果即可,那么不需要另外声明数组保存数据,只需要直接⼀个for循环求得n阶约瑟夫环问题的结果即可由于往往现实⽣活中编号是从1-n,那么我们把最后的结果加1即可。
约 瑟 夫 环 问 题 的 三 种 解 法
约瑟夫问题(数学解法及数组模拟)约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。
在计算机编程的算法中,类似问题又称为约瑟夫环。
又称“丢手绢问题”.)据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus 和他的朋友并不想遵从。
首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。
接着,再越过k-1个人,并杀掉第k个人。
这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。
问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
? 以上来自百度百科约瑟夫问题是个很有名的问题:N个人围成一个圈,从第一个人开始报数,第M个人会被杀掉,最后一个人则为幸存者,其余人都将被杀掉。
例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
约瑟夫问题其实并不难,但求解的方法多种多样;题目的变化形式也很多。
接下来我们来对约瑟夫问题进行讨论。
1.模拟解法优点 : 思维简单。
?缺点:时间复杂度高达O(m*n)当n和m的值较大时,无法短时间内得到答案。
为了叙述的方便我们将n个人编号为:1- n ,用一个数组vis 来标记是否存活:1表示死亡 0表示存活 s代表当前死亡的人数? cnt 代表当前报了数的人数用t来枚举每一个位置(当tn时 t=1将人首尾相连)? 那么我们不难得出核心代码如下:bool vis[1000]; --标记当前位置的人的存活状态int t = 0; --模拟位置int s = 0; --死亡人数int cnt = 0; --计数器if(t n) t = 1;if(!vis[t]) cnt++; --如果这里有人,计数器+1if(cnt == m) --如果此时已经等于m,这这个人死去cnt = 0; --计数器清零s++; --死亡人数+1vis[t] = 1 --标记这个位置的人已经死去coutt" "; --输出这个位置的编号}while(s != n);接下来我们来看另一种更为高效快速的解法数学解法我们将这n个人按顺时针编号为0~n-1,则每次报数到m-1的人死去,剩下的人又继续从0开始报数,不断重复,求最后幸存的人最初的编号是多少?我们只需要将最后求得的解加1就能得到原来的编号。
约瑟夫斯问题
约瑟夫斯问题
问题描述
约瑟夫斯问题是一个经典的数学问题,也被称为约瑟夫环问题。
问题的描述如下:有n个人围成一圈,从第一个人开始报数,报到m的人出列,然后从出列的下一个人开始重新报数,再次报到m的人出列,如此循环,直到所有人都出列为止。
那么,最后剩下的人在原来的顺序中编号是几?
算法思路
为了解决约瑟夫斯问题,可以使用一种常用的数学技巧来计算最后剩下的人的编号。
假设n个人的编号分别为0, 1, 2, …, n-1,那么可以得到一个递推公式:
f(n, m) = (f(n-1, m) + m) % n
其中f(n, m)表示有n个人时最后剩下的人的编号。
根据这个递推公式,可以进行递归计算。
算法实现
下面是使用Python语言实现约瑟夫斯问题的算法:
```python def josephus(n, m): if n == 1: return 0 else: return (josephus(n-1, m) + m) % n
测试样例
n = 7 # 总人数 m = 3 # 报数到m时出列 survivor = josephus(n, m) print(。
约瑟夫环问题的两种解法(循环链表和公式法)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述:N people form a circle, eliminate a person every k people, who is the final survior?Label each person with 0, 1, 2, ..., n - 1, denote(表⽰,指代) J(n, k) the labels of surviors when there are n people.(J(n, k)表⽰了当有 n 个⼈时幸存者的标号)First eliminate the person labeled k - 1, relabel the rest, starting with 0 for the one originally labeled k.0 1 2 3 ... k-2 k-1 k k+1 ... n-1... k-2 0 1 ...Dynamic programmingJ(n, k) = J(J(n - 1, k) + k) % n, if n > 1,J(1, k) = 0⽤中⽂的⽅式简单翻译⼀下就是 (吐槽:为啥课上不直接⽤中⽂呢?淦!) 有 n 个⼈围成⼀圈,从第⼀个⼈开始,从 1 开始报数,报 k 的⼈就将被杀死,然后从下⼀个⼈开始重新从 1 开始报数,往后还是报 k 的⼈被杀掉,杀到最后只剩⼀个⼈时,其⼈就为幸存者。
(上⾯的英⽂是从 0 开始的,是因为我们写程序时使⽤了数组,所以下标从 0 开始)解决⽅案循环链表⽅法算法思路很简单,我们这⾥使⽤了循环链表模拟了这个过程:节点 1 指向节点 2,节点 2 指向节点 3,...,然后节点 N 再指向节点 1,这样就形成了⼀个圆环。
如图所⽰,n 取 12,k 取 3,从 1 开始报数,然后依次删除 3, 6, 9, 12:#include<stdio.h>#include<stdlib.h>typedef struct Node // 节点存放⼀个数据和指向下⼀个节点的指针{int data;struct Node *next;} *NList; // NList为指向 Node 节点的指针// 创建⼀个节点数为 n 的循环链表NList createList(int n){// 先创建⼀个节点NList p, tmp, head;p = (NList)malloc(sizeof(struct Node));head = p; // 保存头节点p->data = 1; // 第⼀个节点for (int i = 2; i <=n ; i++){tmp = (NList)malloc(sizeof(struct Node));tmp->data = i;p->next = tmp;p = tmp;}p->next = head; // 最后⼀个节点指回开头return head;}// 从编号为 1 的⼈开始报数,报到 k 的⼈出列,被杀掉void processList(NList head, int k){if (!head) return;NList p = head;NList tmp;while (p->next != p){for (int i = 0; i < k - 1; i++){tmp = p;p = p->next;}printf("%d 号被杀死\n", p->data);tmp->next = p->next;free(p);p = NULL; // 防⽌产⽣野指针,下同p = tmp->next;}printf("幸存者为 %d 号", p->data);free(p);p = NULL;}int main(){NList head = createList(11);processList(head, 3);return 0;}测试结果:易知,这个算法的时间复杂度为O(nk),显然,这不是⼀个好的算法。
约瑟夫问题数据结构实验报告
约瑟夫问题数据结构实验报告[正文]1.实验目的本实验的目的是分析约瑟夫问题,并设计合适的数据结构解决该问题。
2.实验背景约瑟夫问题,又称为约瑟夫环,是一个经典的数学问题。
问题描述如下:有n个人围成一圈,从第一个人开始报数,数到第m个人时将其杀死,然后从下一个人开始重新报数,数到第m个人又将其杀死,如此循环进行,直到所有人都被杀死为止。
求出最后一个被杀的人在初始序列中的编号。
3.实验设计为了解决约瑟夫问题,我们需要设计合适的数据结构来表示这个过程。
以下为实验所采用的数据结构:3.1 线性表由于约瑟夫问题是围成一圈的,因此我们选择使用循环链表来表示人围成的圈。
每个节点代表一个人,包含一个成员变量用于存储人的编号。
3.2 算法采用如下算法来解决约瑟夫问题:1.创建一个循环链表,将n个人的编号分别存入节点中。
2.初始化一个指针p指向链表的第一个节点。
3.从第一个人开始报数,每报到第m个人,将该节点从链表中删除。
4.如果链表中只剩下一个节点,此时的节点即为最后一个被杀的人,输出其编号。
4.实验步骤4.1 数据结构设计根据实验设计中的描述,我们编写了一个含有循环链表和节点的数据结构。
```cppstruct ListNode {int number;ListNode next;};```4.2 实现约瑟夫问题算法根据实验设计中的算法描述,我们编写了解决约瑟夫问题的函数。
```cppint josephusProblem(int n, int m) {// 创建循环链表// 初始化指针p// 开始报数并删除节点// 返回最后被杀的人的编号}```4.3 测试与分析我们通过输入不同的n和m值,测试了约瑟夫问题的解决函数,并对实验结果进行了分析。
5.实验结果经过测试,我们得到了约瑟夫问题的解。
6.实验总结通过本实验,我们深入了解了约瑟夫问题,并成功设计了合适的数据结构和算法解决了该问题。
附件本文档无附件。
法律名词及注释1.约瑟夫问题:亦称为约瑟夫环问题,是一个数学难题,起源于古代历史记载,已有几个世纪的历史。
一、Josephu约瑟夫问题
⼀、Josephu约瑟夫问题Description约瑟夫问题:有n个⼈,按顺时针⽅向围成⼀圈(编号从1到n),从第1号开始报数,⼀直数到m,数到m的⼈退出圈外,剩下的⼈再接着从1开始报数。
,编程求输⼊n,m后,输出出圈的序号。
Input每⾏是⽤空格分开的两个整数,第⼀个是 n, 第⼆个是 m ( 0 < m,n <=300)。
最后⼀⾏是:0 0Output输出出队的队列package datastruct.josephu;public class Josephu {public static void main(String[] args) {// ⾸先找到第⼀个节点的前⼀个节点。
Node head = new Node(1);head.addNode(2).addNode(3).addNode(4).addNode(5).addNode(6).addNode(7).addNode(8).next = head;jsephuResult(head, 8, 3);}public static void jsephuResult(Node head, int n, int m) {// step 1: 找到第⼀个节点的前⼀个节点Node temp = head;while (temp.next != head) {temp = temp.next;}while (temp.next != temp) {for (int i = 0; i < m - 1 && head.next != head; i++) {temp = temp.next;}// output and delete nodeSystem.out.println(temp.next.value);temp.next = temp.next.next;}System.out.println(temp.value);}}class Node {Node next = null;int value;Node(int v) {value = v;}Node addNode(int value) {Node nextNode = new Node(value);this.next = nextNode;return nextNode;}}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
}
printf("%d ",tmp->number);
time = tmp->pass - 1;
deleteFromList(&head,tmp);//删除结点
tmp = tmp->next;//从下一个结点又开始计算
}
}
int main(int argc, char *argv[])
{
int i;
Node *p;
int num[7] = {1,2,3,4,5,6,7};
int pas[7] = {3,1,7,2,4,8,4};
List head;
head = (List)malloc(sizeof(List));
initList(head);
createFromTail(head,num,pas,sizeof(num)/sizeof(num[0]));
pri = head ;
for(i = 1;i<length;i++){
tmp = (Node *)malloc(sizeof(Node));
tmp->number = num[i];
tmp->pass = pas[i];
pri->next = tmp;
pri = tmp;
pri->next = head;
}
}
//从链表中删除
void deleteFromList(List *head,Node *tmp)
{
Node *tmp1;
Node *tmp2;
tmp1 = *head;
tmp2 = tmp1;
//如果链表剩了一个元素
if(tmp->next == tmp){
tmp->number = 0 ;
tmp->pass = 0;
实验一 约瑟夫问题求解
学号
131842352
姓名
朱家军
班级
软1342
实验名称
线性表结构及其应用
实验内容
约瑟夫环问题
实
验
目
的
和要Leabharlann 求实验目的:1、掌握上机调试线性表的基本方法;
2、掌握线性表的基本操作:插入、删除、查找等运算在顺序存储结构和链式存储结构上的运算。
3、掌握约瑟夫问题求解算法。
实验内容:1、认真阅读和掌握本实验的参考程序,并上机运行本程序,可尝试完善删除、查找等运算。
代码实现:
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int number;
int pass;
struct node *next;
}Node,*List;
//初始化循环单链表
void initList(List L)
{
L->number = 0;
return;
}
//如果此时删除了头节点,此时将头节点转移到头节点的下一个结点
if((*head)->number == tmp->number){
Node *x;
Node *y;
x = (*head)->next;
while(x != (*head)){
y = x;
x = x->next;
}
y->next = (*head)->next;
L->pass = 0;
L->next = L;
}
//尾插法建立循环单链表
void createFromTail(List head,int num[],int pas[],int length)
{
int i;
Node *pri;
Node *tmp;
head->number = num[0];
head->pass = pas[0];
*head = (*head)->next;
}else{
//找到这个节点,删除他
while(tmp1->number != tmp->number){
tmp2 = tmp1;
tmp1 = tmp1->next;
}
tmp2->next = tmp1->next;
}
}
//约瑟夫计数
void yuesefu(List head, int m)
2、实验报告要求:(1)算法的完整代码;(2)程序运行结果及分析;(3)实验总结。
基本要求:利用单向循环链表存储结构模拟此过程,按照出列的顺序输出各人的编号。
问
题
描
述
和
主
要
步
骤
问题描述:
约瑟夫问题:编号为1,2,..n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序来求出出列顺序,并输出结果。
2、参考第二章课件中对瑟夫问题求解算法的描述,选择一种存储结构实现该问题的求解。
注意事项:1、在磁盘上创建一个目录,专门用于存储数据结构实验的程序,可通过U盘或邮箱长期保存程序。
2、实验报告要求:(1)算法的完整代码;(2)程序运行结果及分析;(3)实验总结。
1、在磁盘上创建一个目录,专门用于存储数据结构实验的程序,可通过U盘或邮箱长期保存程序。
printf("\n");
return 0;
}
实
验
结
果
(
截
图
表
示
)
研
究
与
探
讨
通过这次实验对单链表有了更深的了解,在实验中还存在一些问题,需要多做练习来加强。
p = head;
printf("\n约瑟夫计数前,每个数和他的密码:\n");
for(i = 0;i<sizeof(num)/sizeof(num[0]);i++){
printf("%d,%d\n",p->number,p->pass);
p = p->next;
}
printf("\n");
yuesefu(head,20);
{
Node *tmp;
tmp = head;
int time;
time = m-1;
int i = 0;
printf("出队的顺序:(初始化的m=20)\n");
while(tmp->number != 0){ //链表中此时没有结点,则退出
for(i=0;i<time;i++){ //找到要删除的结点