c语言约瑟夫问题,输出序号算法

合集下载

约瑟夫问题面向对象(C++版)程序设计报告

约瑟夫问题面向对象(C++版)程序设计报告
Jose类中的成员函数分别为:
函数
相关说明
Jose(int boys, int interval, int begin=1);
构造函数,主要功能初始化各个参数,校验输出的m、n、s参数为int boys, int interval, int begin=1
void getWinner()const;
//构造函数
//-------------------------------------
BoyRing::BoyRing(int n)
{
if(n<2) throw exception(); //发生错误时自动跳出
pBegin = new Boy[n]; //申请一个新的Boy空间,pBegin指向该空间的开始处
for(int i=1; i<=n; i++)
{
pBegin[i-1].next = &pBegin[i%n];//形成链表
pBegin[i-1].code = i;//给孩子孩子编号
}
pivot = pCurrent = &pBegin[n-1];//各指针初始化
}
//------------------------------------
2详细设计
2.1程序结构
改程序分成三个抽象层次,第一层是应用层,直接指使Jose类对象去完成求解工作;第二层是Jose类层,描述Josephus问题的求解细节;第三层是BoyRing类层,辅助Jose类完成求解过程(上述三层间的关系描述如图J-1,这也是程序模块间的关系)。
图J-1.对象实现中程序模块的相互关系
void disengage();
利用指针实现小孩离队,无参数

约瑟夫环问题实验报告

约瑟夫环问题实验报告

约瑟夫问题实验报告背景约瑟夫问题(Josephus Problem)据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

原题:用户输入M,N值,N个人围成一个环,从0号人开始数,数到M,那个人就退出游戏,直到最后一个人求最后一个剩下的人是几号?问题描述设编号为1-n的n(n>0)个人按顺时针方向围成一圈.首先第1个人从1开始顺时针报数.报m的人(m 为正整数).令其出列。

然后再从他的下一个人开始,重新从1顺时针报数,报m的人,再令其出列。

如此下去,直到圈中所有人出列为止。

求出列编号序列。

一.需求分析:(1)基本要求需要基于线性表的基本操作来实现约瑟夫问题需要利用循环链表来实现线性表(2)输入输出格式输入格式:n,m(n,m均为正整数,)输出格式1:在字符界面上输出这n个数的输出序列(3)测试用例(举例)输入:8,4输出:4 8 5 2 1 3 7 6二.概要设计(1)抽象数据类型:数据对象:n个整数数据关系:除第一个和最后一个n外,其余每个整数都有两个元素与该元素相邻。

基本操作:查找,初始化,删除,创建链表循环链表的存储结构:(2).算法的基本思想循环链表基本思想:先把n个整数存入循环链表中,设置第m个数出列,从第一个开始查找,找到第m个时,输出第m个数,并删掉第m个节点,再从下一个数开始查找,重复上一步骤,直到链表为空,结束。

(3).程序的流程程序由三个模块组成:1.输入模块:完成两个正整数的输入,存入变量n和m中2.处理模块:找到第m个数3.输出模块:按找到的顺序把n个数输出到屏幕上三.详细设计首先,设计实现约瑟夫环问题的存储结构。

C语言经典算法大全

C语言经典算法大全

C语言经典算法大全➢老掉牙河内塔费式数列巴斯卡三角形三色棋老鼠走迷官(一)老鼠走迷官(二)骑士走棋盘八个皇后八枚银币生命游戏字串核对双色、三色河内塔背包问题(Knapsack Problem)➢数、运算蒙地卡罗法求PIEratosthenes筛选求质数超长整数运算(大数运算)长PI最大公因数、最小公倍数、因式分解完美数阿姆斯壮数最大访客数中序式转后序式(前序式)后序式的运算➢关于赌博洗扑克牌(乱数排列)Craps赌博游戏约瑟夫问题(Josephus Problem)➢集合问题排列组合格雷码(Gray Code)产生可能的集合m元素集合的n个元素子集数字拆解➢排序得分排行选择、插入、气泡排序Shell 排序法—改良的插入排序Shaker 排序法- 改良的气泡排序Heap 排序法—改良的选择排序快速排序法(一)快速排序法(二)快速排序法(三)合并排序法基数排序法➢搜寻循序搜寻法(使用卫兵)二分搜寻法(搜寻原则的代表) 插补搜寻法费氏搜寻法➢矩阵稀疏矩阵多维矩阵转一维矩阵上三角、下三角、对称矩阵奇数魔方阵4N 魔方阵2(2N+1) 魔方阵1.河内之塔说明河内之塔(Towers of Hanoi)是法国人M。

Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学家Edouard Lucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒(Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disc),并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损,而也就是世界末日来临之时.解法如果柱子标为ABC,要由A搬至C,在只有一个盘子时,就将它直接搬至C,当有两个盘子,就将B当作辅助柱。

如果盘数超过2个,将第三个以下的盘子遮起来,就很简单了,每次处理两个盘子,也就是:A->B、A -〉C、B—>C这三个步骤,而被遮住的部份,其实就是进入程式的递回处理。

C语言编程题目000

C语言编程题目000

题1. 统计字母的使用频率一、题目:统计字母的使用频率二、目的与要求1.目的:通过编写程序统计字母的使用频率,培养学生综合利用C语言进行程序设计的能力,熟悉字符串的操作方法,加强函数的运用,提高软件系统分析能力和程序文档建立、归纳总结的能力。

2.基本要求:1)要求用C语言编程,在Visual C++环境下调试完成;2)要求按照程序功能分成几个功能模块来实现,各个功能模块分别使用函数来完成;3)要求应用本课所讲授的程序设计语言知识来解决问题三、设计方法和基本原理1.课题功能描述本程序的功能,就是要统计英文字母的使用频率。

2.问题详细描述为统计英文字母的使用频率,输入一个不包括空格的由英文字母组成的字符串,长度不超过200个字符。

统计26个英文字母的使用频率,不区分大小写。

最后按使用频率从大到小输出字母(小写字母)和使用频率(出现的次数)。

3.问题的解决方案按照程序要求,本程序应采用模块化设计方法,设计几个功能模块。

例如(仅供参考):l 将字符串中的大写字母转换为小写字母l 统计输入的字符串中字母的使用频率l 按使用频率从大到小进行排序主函数中控制输入、函数调用和输出。

四、主要技术问题的描述根据三的分析,主要问题在于:1) 为统计字母的使用频率,定义一个长度为26的int数组存放所统计的各个字母的使用频率。

2) 在统计字母的使用频率时,不要使用if语句或switch语句,利用字母的ASCII码与数组元素下标之间的关系来求得。

3) 按使用频率从大到小进行排序时,建议使用指针数组更为方便。

五、创新要求实现程序功能后,可进行创新设计:1) 使用多文件,即主函数和各个函数分别存放在不同的.c文件中,在头文件中进行函数原型声明。

2) 读入一篇英文文档,并对其进行字母频率分析。

题2.指示灯控制问题描述:N盏灯排成一排,从1到N按顺序依次编号。

有N个人也从1到N依次编号。

第一个人(1号)将灯全部关闭。

第二个人(2号)将凡是2和2的倍数的灯打开。

约瑟夫环问题源代码(C语言)

约瑟夫环问题源代码(C语言)

约瑟夫环问题如下:已知n个人(n>=1)围桌一园桌周围,从1开始顺序编号。

从序号为1的人开始报数,顺时针数到m的那个人出列。

他的下一个人又从1开始报数,数到m的那个人又出列。

依此规则重复下去,直到所有人全部出列。

求解最后一个出列的人的编号。

本次实验是以顺序表求解约瑟夫环问题,程序流程图及程序运行结果如下:输入人数、所报数、第一个报数人编号存储并建立一个约瑟夫环通过循环结构依次查找每次出列的人的编号并输出输出最后一个出列的人的编号程序代码如下:#include<iostream>#include<process.h>#include<stdlib.h>using namespace std;struct Node //循环节点的定义{int number; //编号Node *next;};Node *CreateList(Node *L,int &n,int &m); //建立约瑟夫环函数void Joseph(Node *L,int n,int m); //输出每次出列号数函数Node *DeleteList(Node **L,int i,Node *q); //寻找每次出列人的号数int LengthList(Node *L); //计算环上所有人数函数void main() //主函数{system("color 75"); //设置颜色以美观Node *L;L=NULL; //初始化尾指针int n, m;cout<<"请输入人数N:";cin>>n; //环的长度if(n<1){cout<<"请输入正整数!";} //人数异常处理else{cout<<"请输入所报数M:";cin>>m;if(m<1){cout<<"请输入正整数!";} //号数异常处理else{L=CreateList(L,n,m); //重新给尾指针赋值Joseph(L,n,m);}}system("pause");}Node *CreateList(Node *L,int &n,int &m) //建立一个约瑟夫环(尾插法){Node *q;for(int i=1;i<=n;i++){Node *p;p=new Node;p->number=i;p->next=NULL;if(i==1) L=q=p; //工作指针的初始化 else{q->next=p;q=q->next;}}q->next=L;if(L!=NULL){return(L);} //返回尾指针else cout<<"尾指针异常!"<<endl; //尾指针异常处理}void Joseph(Node *L,int n,int m) //输出每次出列的人{int k;cout<<"请输入第一个报数人:";cin>>k;if(k<1||k>n){cout<<"请输入1-"<<n<<"之间的数"<<endl;}else{cout<<"\n出列顺序:\n";for(int i=1;i<n;i++){Node *q = new Node;if(i==1) q=DeleteList(&L,k+m-1,q); //第一个出列人的号数else q=DeleteList(&L,m,q);cout<<"号数:"<<q->number<<endl;delete q; //释放出列人的存储空间}cout<<"最后一个出列号数是:"<<L->number<<endl; //输出最后出列人的号数}}Node *DeleteList(Node **L,int i,Node *q) //寻找每次出列的人{if(i==1) i+=LengthList(*L); //顺序依次出列情况的处理方式Node *p;p=*L;int j=0;while(j<i-2) {p=p->next;j++;}q = p->next;p->next=p->next->next;*L = p->next;return(q);}int LengthList(Node *L) //计算环上的人数{if(L){cout<<"尾指针错误!"<<endl;} //异常处理else{int i=1;Node *p=L->next;while(p!=L){i++;p=p->next;}return(i);}}实验体会:通过对本问题的分析,我进一步熟悉了对各种逻辑表达式的判断和指针的使用。

约瑟夫问题大全

约瑟夫问题大全

“约瑟夫”问题及若干变种林厚从例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),对于极限数据会超时。

约瑟夫问题

约瑟夫问题

约瑟夫问题一、问题描述和要求1、问题描述约瑟夫问题是这样的:设有n个人围圆桌坐成一圈,现从第s个人开始报数,数到m的人出列,接着从出列的下一个人开始重新报数,数到m的人又出列,如此重复下去,直到所有人都出列为止。

2、程序设计要求(1)、通过本学期《数据结构》的学习,综合运用已学过的理论和技能去分析和解决约瑟夫问题,加深对数据结构课程理论的理解和运用、切实加强自己的实践动手能力和创新能力。

(2)、结合C语言程序设计、数据结构中所学的理论知识,小组独立设计方案,培养分析与解决问题的能力。

(3)、学会查阅相关手册和资料,进一步熟悉常用算法的用途和技巧,掌握这些算法的具体含义。

(4)、认真调试程序,学会改正程序中的错误,尝试不同的方法实现同一功能,培养严谨的作风和科学的态度。

二、个人所负责的工作1、算法思路约瑟夫问题的解决可以用线性链表,循环链表,数组等多种方法,我们采用线性链表解决该问题,采用分块的设计思路,利用函数调用解决问题。

本算法采用for循环解决线性链表的建立,用printf函数输出链表,利用for循环找到第s个人开始报数,再利用一个for循环找到第m个人,然后多重if语句讨论不同情况的处理,最后通过函数调用解决约瑟夫问题。

2、我负责的工作在这次程序设计中,我主要负责对整个题目设计思路进行分析和对程序的注释。

我们刚设计出来的程序存在着种种问题,各部分程序都需要进行一定的修改和完善,比如线性链表不能够正常输入,主函数对用户的输入要求的描述太过笼统,输出函数不能调用等等。

我就是不断发现这些问题并完善它们,使程序能够正确的运行。

然后我和其他组员经过商议,对程序的各部分含义进行注释。

在程序演示时我主要负责对整个题目设计思路分析及组员介绍。

以下是我修改过的程序之一:NODE *creatlinklist(int n)/*建立线性链表*/{ int i;NODE *head,*p,*q;/*定义整形实参i,指针head、p、q*/if(n==0)return(NULL);/*如果n=0建立一个空链表*/elsehead=(NODE *)malloc(sizeof *head);/*申请一个结点为表头*/q=head;for (i=1;i<=n;i++)/*将n个结点输入到单链表中*/{p=(NODE *)malloc(sizeof *head);printf("输入数据:");p->info=getche();/*getche功能: 输入后立即从控制台取字符,不以回车为结束*/printf("\n");q->next=p;/*q指向表尾,准备导入下一个结点*/q=p;}p->next=NULL;/*将最后一个结点的链域置为空*/return(head);}三、结论经过这次程序设计任务,我认真的复习了数据结构中学习过的理论知识,尤其是对线性链表这一部分进行了认真的归纳总结,对数据结构的认识更深刻了,对线性链表的建立、插入、删除、查找等操作的运用更加熟练,对for循环、输入输出函数等更加了解,而且我意识到今后我可以利用这些函数解决更多的实际问题。

C语言的循环链表和约瑟夫环

C语言的循环链表和约瑟夫环

C语言的循环链表和约瑟夫环C语言的循环链表和约瑟夫环约瑟夫问题)是一个数学的应用问题,对于学习C语言四非常挺有帮助的,下面是店铺为大家搜集整理出来的有关于C语言的循环链表和约瑟夫环,一起了解下吧!循环链表的实现单链表只有向后结点,当单链表的尾链表不指向NULL,而是指向头结点时候,形成了一个环,成为单循环链表,简称循环链表。

当它是空表,向后结点就只想了自己,这也是它与单链表的主要差异,判断node->next是否等于head。

代码实现分为四部分:1. 初始化2. 插入3. 删除4. 定位寻找代码实现:1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1void ListInit(Node *pNode){int item;Node *temp,*target;cout<<"输入0完成初始化"<<endl; cin="">>item;if(!item)return ;if(!(pNode)){ //当空表的时候,head==NULLpNode = new Node ;if(!(pNode))exit(0);//未成功申请pNode->data = item;pNode->next = pNode;}else{//for(target = pNode;target->next!=pNode;target = target->next);4 15 16 17 18 19 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3temp = new Node;if(!(temp))exit(0);temp->data = item;temp->next = pNode;target->next = temp;}}}void ListInsert(Node *pNode,int i){ //参数是首节点和插入位置Node *temp;Node *target;int item;cout<<"输入您要插入的值:"<<endl; cin="">>item;if(i==1){temp = new Node;if(!temp)exit(0);temp->data = item;for(target=pNode;target->next != pNode;target = target->next);temp->next = pNode;target->next = temp;pNode = temp;}else{target = pNode;for (int j=1;j<i-1;++j) target="target-">next;temp = new Node;if(!temp)exit(0);temp->data = item;temp->next = target->next;target->next = temp;}}void ListDelete(Node *pNode,int i){Node *target,*temp;if(i==1){for(target=pNode;target->next!=pNode;target=target ->next);temp = pNode;//保存一下要删除的首节点 ,一会便于释放6 37 38 39 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5pNode = pNode->next;target->next = pNode;temp;}else{target = pNode;for(int j=1;j<i-1;++j) target="target-">next;temp = target->next;//要释放的nodetarget->next = target->next->next;temp;}}int ListSearch(Node *pNode,int elem){ //查询并返回结点所在的位置Node *target;int i=1;for(target = pNode;target->data!=elem && target->next!= pNode;++i)target = target->next;if(target->next == pNode && target->data!=elem)return 0;else return i;}</i-1;++j)></i-1;++j)></endl;></endl;>5 96 0 6 1 6 2 6 3 6 4 6 5 6 6 67 68 69 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8约瑟夫问题约瑟夫环(约瑟夫问题)是一个数学的'应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。

约瑟夫问题详解(CC++)

约瑟夫问题详解(CC++)

约瑟夫问题详解(CC++)Josephus 约瑟夫问题假设n个竞赛者排成一个环形,依次顺序编号1,2,…,n。

从某个指定的第1号开始,沿环计数,每数到第m个人就让其出列,且从下一个人开始重新计数,继续进行下去。

这个过程一直进行到所有的人都出列为止。

最后出列者为优胜者。

无论是用链表实现还是用数组实现来解约瑟夫问题都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较麻烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

注意到原问题仅仅是要求出最后的胜利者的序号,而不是要模拟整个过程。

因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:问题描述: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 --> 0k+1 --> 1k+2 --> 2......k-2 --> n-2变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x 是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?变回去的公式很简单:x'=(x+k)%n如何知道(n-1)个人报数的问题的解?显然,只要知道(n-2)个人的解就行了。

(n-2)个人的解呢?当然是先求(n-3)的情况---- 这显然就是一个倒推问题!递推公式:令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]递推公式f[1]=0;f[i]=(f[i-1]+m)%i; (i>1)有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。

顺序表实现约瑟夫环的问题,C语言

顺序表实现约瑟夫环的问题,C语言

顺序表实现约瑟夫环的问题,C语言计算机科学与工程学院《算法与数据结构》试验报告[一] 专业班级 10级计算机工程02 试验地点计算机大楼计工教研室学生学号 1005080222 指导教师蔡琼学生姓名肖宇博试验时间 2012-2-29试验项目算法与数据结构试验类别基础性() 设计性() 综合性(?) 其它( )(1)掌握用VC++上机调试线性表的基本方法; 试(2)掌握顺序表的存储结构以及基本运算的实现。

验目的及要求成绩评定表类别评分标准分值得分合计积极出勤、遵守纪律上机表现 30分主动完成设计任务程序代码规范、功能正确程序与报告 70分报告详实完整、体现收获备注:评阅教师:日期: 年月日计算机科学与工程学院试验内容一、实验目的和要求1、实验目的:(1)掌握用VC++上机调试线性表的基本方法;(2)掌握顺序表的存储结构以及基本运算的实现。

2、实验内容约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个人按顺时针方向围坐一圈,m为任意一个正整数。

从第一个人开始顺时针方向自1起顺序报数,报到m时停止并且报m的人出列,再从他的下一个人开始重新从1报数,报到m时停止并且报m的人出列。

如此下去,直到所有人全部出列为止。

要求设计一个程序模拟此过程,对任意给定的m和n,求出出列编号序列。

3、实验要求:用顺序表实现。

二、设计分析根据实验要求,采用顺序表来完成本次实验。

实验中定义了两个顺序表,一个用来存储n个人的序号,另一个用来存储n个人的出队顺序及序号。

程序中充分考虑了如果出队的元素大于队列的元素个数时应该有的情况,如果出现这样的错误就提示~否则继续出队~三、源程序代码#include<stdio.h>#include<stdlib.h>#define MAXSIZE 10 // 宏替换最大值typedef struct{int data[MAXSIZE];int length;}Sqlist;void CreatList(Sqlist *&L,int a[],int n) //创建顺序表{L=(Sqlist *)malloc(sizeof(Sqlist));for(int i=0;i<n;i++){L->data[i]=a[i];}L->length=n;}void InitList(Sqlist *&L) //初始化顺序表{2 《算法与数据结构》试验报告计算机科学与工程学院L=(Sqlist *)malloc(sizeof(Sqlist));L->length=0;}void DestoryList(Sqlist *&L) //释放顺序表空间{free(L);}void josephus(Sqlist *&L) //约瑟夫环的核心代码{int t=0;int m=0;printf("请输入数到几个人出来");printf("\n");scanf("%d",&m);if(m>L->length){printf("没有这么多人呀~?(?_?)?");}else{printf("出列顺序为:");for(int q=L->length;q>=1;q--){t=(t+m-1)%q;printf("\n");printf("\t%d\t",L->data[t]);for(int j=t+1;j<=q-1;j++)L->data[j-1]=L->data[j];}printf("\n");}}void main(){Sqlist *s;InitList(s);int a[MAXSIZE];int n=0;printf("请键入要输入几个数"); printf("\n");scanf("%d",&n);for(int i=0;i<n;i++)3 《算法与数据结构》试验报告计算机科学与工程学院{a[i]=i+1;}CreatList(s,a,n);josephus(s);DestoryList(s);printf("\n");}四、测试用例(尽量覆盖所有分支) 1.当输入1,2,3,4。

C语言程序设计漫谈之从“约瑟夫问题”谈起

C语言程序设计漫谈之从“约瑟夫问题”谈起

从“约瑟夫问题”谈起约瑟夫问题是一个出现在计算机科学和数学中的问题。

在计算机编程的算法中,类似问题又称为约瑟夫环。

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想自杀。

为避免与其他39个决定自杀的犹太人发生冲突,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行,直到仅余15个人为止。

问怎样的排法,才能使每次投入大海的都是非教徒。

【例1】约瑟夫问题。

N个人围成一圈,从某个人开始,按顺时针方向从1开始依次编号。

从编号为1的人开始顺时针“1,2,…M”报数,报到M的人退出圈子。

这样不断循环下去,圈子里的人将不断减少。

由于人的个数是有限的,因此最终会剩下一个人,该人就是优胜者。

输入N和M,输出出圈顺序。

例如,N=6、M=5,出圈的顺序是:5,4,6,2,3,1。

(1)编程思路。

为输出出圈顺序,采用一个数组来进行模拟。

定义int circle[N+1],并按circle[i]=i+1的方式赋予各元素初值。

该值代表两个含义:1)值为0,代表编号i+1的人不再圈中;2)值非0,代表圈中第i个位置的人编号为i+1。

定义变量i代表报数位置的流动,i的初值为0,代表编号为1的人的位置,i的变化方式为:i=(i+1)%(n),即0-->1-->2……->n-1 ->0-->1……。

题目约瑟夫(Joseph)问题的一种描述是编号为1,2,,

题目约瑟夫(Joseph)问题的一种描述是编号为1,2,,

题目:约瑟夫(Joseph)问题的一种描述是:编号为1,2,......,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

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

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

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

班级:姓名:学号:完成日期:一、需求分析1.本演示程序中,利用单向循环链表存储结构存储约瑟夫环数据(即n个人的编号和密码)。

2.演示程序以用户和计算机的对话方式执行,即在计算机终端上显示"提示信息"之后,由用户在键盘上输入演示程序中需要输入的数据,运算结果显示在其后。

3.程序执行的命令包括:1)构造单向循环链表;2)4.测试数据m 的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6(正确的出列顺序为6,1,4,7,2,1,3,5)。

二、概要设计1.单向循环链表的抽象数据类型定义为:ADT List{数据对象:D={ai | ai∈正整数,I=1,2,......,n,n≥0}数据关系:R1={< ai-1,ai > |,ai-1,ai∈D,I=1,2,......,n}基本操作:Init List(&L)操作结果:构造一个空的线性表L。

List Insert(&L,i,e)初始条件:线性表L已存在,1≤i≤List Length(L)+1.操作结果:在L中第i个位置之前插入新的数据无素e,L长度加1。

List Delete(&L,i,&e)初始条件:线性表L存在非空,1≤i≤List Length(L).操作结果:删除L的第i个元素,并用e返回其值,L长度减1。

2.程序包含四个模块:1)主程序模块:三详细设计typedef struct LNode{int password; //密码int No; //序号struct LNode *next; //下一成员指针}member; //成员结构体typedef int status;#define OVERFLOW -2#define OK 1#define ERROR 0#include <stdio.h>#include <stdlib.h>status CreateList_Circle(member **,int);status DeleteNode(member **);status main(){int n,m;member *head=NULL,*p=NULL; //头指针即首成员地址,遍历指针p printf ("Please enter number of people:\n");scanf ("%d",&n); //总成员数while (n<=0){printf ("n must be positive, please enter again:\n");scanf ("%d",&n);}if(!CreateList_Circle(&head,n)) //创建循环链表,返回头指针head return OVERFLOW;printf ("Please enter initial m:\n");scanf ("%d",&m); //初始mwhile (m<=0){printf ("m must be positive, please enter again:\n");scanf ("%d",&m);}printf ("\nThe order is:\n");p=head;while (n>=2) //寻找出列成员{int i;m=(m%n==0)?n:m%n; //化简m值for (i=1;i<m;i++)p=p->next; //p指向出列成员printf ("%d\n",p->No); //输出出列成员序号m=p->password; //修改mDeleteNode(&p); //删除链表中的出列成员n--; //成员数自减}printf ("%d\n",p->No); //输出最后一个成员序号return OK;}status CreateList_Circle(member **p_head,int n){//此算法创建一个无头结点的循环链表,结点数n,*p_head返回链表头指针即首结点地址int i;member *tail,*p;*p_head=(member *)malloc(sizeof(member));if (!(*p_head)) return OVERFLOW;(*p_head)->No=1; //储存成员一序号printf ("Please enter password of No. 1:\n");scanf ("%d",&(*p_head)->password); //储存成员一密码tail=*p_head;tail->next=NULL;for (i=2;i<n+1;i++){p=(member *)malloc(sizeof(member));if (!p) return OVERFLOW;p->No=i; //储存成员序号printf ("Please enter password of No. %d:\n",i);scanf("%d",&(p->password)); //储存成员密码tail->next=p;tail=p;}tail->next=*p_head;return OK;}status DeleteNode(member **pp){//此算法删除链表中的结点*pp,操作实质是将*pp下一结点复制到*pp后将其free member *temp;(*pp)->password=((*pp)->next)->password;(*pp)->No=((*pp)->next)->No;temp=(*pp)->next;(*pp)->next=(*pp)->next->next;free(temp);return OK;}四、调试分析程序的编写和调试基本正常。

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫问题(数学解法及数组模拟)约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。

在计算机编程的算法中,类似问题又称为约瑟夫环。

又称“丢手绢问题”.)据说著名犹太历史学家 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就能得到原来的编号。

约瑟夫问题的解法及引发的整数进制问题思考

约瑟夫问题的解法及引发的整数进制问题思考

约瑟夫问题的解法及引发的整数进制问题思考【摘要】:约瑟夫(Josephus)问题(也称约瑟夫斯置换,约瑟夫环问题),是一个出现在计算机科学和数学中的经典问题. 本文将就约瑟夫问题的算法复杂度优化、纯数学解法和引申出的数论进制问题进行讨论和创新,后两部分为本文研讨的重点,即离散数学中数论方面的进制问题.【关键词】:约瑟夫(Josephus)问题,算法优化与纯数学,整数p进制及其应用.首先让我们来回顾一下经典约瑟夫问题的表述:著名犹太历史学家约瑟夫有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人宁死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止. 约瑟夫将朋友与自己安排在第16个与第31个位置,逃过了这场死亡游戏.将其抽象为一般数学问题(抽杀问题)则为:N(n)个人围成一圈,从第一个开始报数,第M(m)个将被杀掉,直至留下最后一个,求剩下的最后一个在原排列中的数码.由其派生的一系列抽杀问题、猴子选王问题在本文不做讨论,重点在于引申的进制问题.1.算法模拟及其复杂度优化1.1正常模拟——复杂度()o nm题目中N个人围成一圈,因而启发我们用一个循环的链来表示. 可以使用结构数组来构成一个循环链. 结构中有两个成员,一个为指向下一个人的指针,以构成环形的链;另一个为该人是否自杀的标记,1表示未被杀. 从第一个人开始对未自杀的人进行计数,每数到9时,将结构中的标记改为0,表示该人已自杀. 这样循环计数直到剩下最后一个.基于程序设计课试写了一段源代码,这次对其无用部分做了缩减和修改(复杂度o(nm)):#include<iostream>using namespace std;void main(){int n, m, a[10001], k, i, j, num;cout << "n = ";cin >> n;if (n>10000){cout << "Beyond!" << endl;return;}cout << "m = ";cin >> m;for (i = 1; i <= n; i++){a[i] = 1;}j = 0;k = 0;for (i = 1; i <= n + 1; i++){if (a[i] == 1) { j = j + a[i]; if (j == m) { j = 0; a[i] = 0; k++;}if (k == n) { num = i; break ; }}if (i == n + 1)i = 0; }cout << "number " << num << " wins " << endl;}1.2一级优化——复杂度()o n对于链表或是数组的模拟形态,在数据较大的N 中我们发现程序的运行大大受制于其复杂度,又考虑到实际上不需要对原题所述的情况进行赋值,故而可以打破常规,先对于原题进行以下的数学分析,再编写一个运行相对快捷的代码.运用数学归纳法的思想,在第一个人mod(n 1)m -出列之后,剩下的1n -个人组成了一个新的约瑟夫环(从mod(n 1)k m =-编号开始),继而我们对新组的编号做一下平移:112211k k k n k n →+→-→--→-对新组的'()mod(n)x x k =+如此一来,我们得到了前后关系的连接媒介,可以根据初值写出如下的关键式:(1)0f =()()mod ,1f f m i i =+>有了这个公式,我们只需要从1n →顺序递推出值,得出[]f n . 由于题目中编号总是从1开始,输出[]1f n +即可. 代码如下(C 语言):#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("number %d wins\n", s + 1);}1.3二级优化(来自网络文献)——复杂度()o m进一步,如果我们观察删去和最终留下的号码,我们会发现它常常处在一种等差的状态n (m=3) 剩余号s16832 4 1 1 17 11 33 7 2 2 18 14 34 10 3 2 19 17 35 13 4 1 20 20 36 16 5 4 21 2 37 19 6 1 22 5 38 22 7 4 23 8 39 25 8 7 24 11 40 28 9 1 25 14 41 31 10 4 26 17 42 34 11 7 27 20 43 37 12 10 28 23 44 40 13 13 29 26 45 43 14 2 30 29 46 46 15 531 147 2观察1.2中的变量s :()%s s m i =+,可以看出,当i 比较大而1s m +-比较小的时候,s 就处于一种等差递增的状态,这个等差递增的过程可以跳过.我们设一中间变量x ,列出如下等式:–1s m x i x +⨯=+解出x ,令s s m x =+⨯,将i x +直接赋值给i ,这样就跳过了中间共x 重的循环,从而节省了等差递增的时间开销. 当求出来的i x +超过n 时,我们便可以结束循环了. 这个方法跳脱出了n 的束缚,复杂度进一步缩减到了(m)o . 以下的代码来自于一篇博文,由于该部分不是本文讨论的重点,没有进行实践:long Josephus(long n,long m,long k) //分别为:人数,出圈步长,起使报数位置, {if (m == 1)k = k == 1 ? n : (k + n - 1) % n; else {for (long i = 1; i <= n; i++) {if ((k + m) < i) {x = (i - k + 1) / (m - 1) - 1; if (i + x < n) {i = i + x;k = (k + m * x); } else {k = k + m * (n - i) ; i = n; } }k = (k + m - 1) % i + 1; } }return k; //返回最后一人的位置}2. 纯数学解答中的归纳与发现2.1对2m =情况的解答想要从纯数学的角度来解答这样一个复杂问题,首先需要从2m =的特殊情况来探讨一些规律和特殊情况.n (m=2) 剩余号s17327 23 8 1 18 5 28 25 9 3 19 7 29 27 10 5 20 9 30 29 11 7 21 11 31 31 12 9 22 13 32 1 13 11 23 15 33 3 14 13 24 17 34 5 15 15 25 19 35 7 16 1262136 9从上表中我们可以发现对于2(k )k ∈ ,始终有1s =,故而易想到将其作为归纳之基,可以得出如下的一种归纳证法:① 2in =时,易得每一次删减处理的数分别为2k ,221k - ,323k -,……,2(23)ik i -- 第s圈删去2i s-个数,最终剩下的是k ∀∈ ,k i ≤,mod(2)k均为1的第一号数码.② 2,(121)i in j j =+≤≤-时,先抽杀j 个数码,接下来剩下的2i个数码是以21j +开头(相当于数码1)的新环列,运用①中的相关结论可得,最终剩下存活的恰是21j +号数码的人. 由①②可以归总得2m =的一般数学解:满足2,(021)i in j j =+≤≤-的约瑟夫环列,最终剩下的数码是21j +这道题的解答是否就这样完成了呢?在进行测算的过程中,我们其实能发现,找出使得2i最接近n 的数字i 是一大关键点. 而恰好在进制转化的过程中,这一问题被交代成了对应二进制数的第一位数问题,故而我将n m 、分别进行了二进制转化,并发现了一定规律:n (10)s (2)n (2)s (2)(2)n s - 8 1 1000 0001 111 9 3 1001 0011 110 10 5 1010 0101 101 11 7 1011 0111 100 12 9 1100 1001 11 13 11 1101 1011 10 14 13 1110 1101 1 15 15 1111 1111 0 161 10000 00001 1111注:(2)n 表示n 的二进制数码,其余类比.就如上述表格中所显示的一样,在二进制数码中,出现了惊人的对称与平移现象,在每一个(2)n 与(2)s 中最“外部”的1保持轴对称,其余数码直接平移就可以完成. 基于其形状可将其命名为“进制平移外称塔”. 仔细想来,二进制数码的第一位即象征十进制解法中的2i,21j +对二进制而言既是末尾加1,整体动作看起来就像将第一位数字移到了末位.有这样的解法的启发,我们是不是能够对任意m 来探讨m 进制下的数码变换而得到更加有新意且简便的解法呢?2.2对3m =情况的讨论基于2.1中的发现,我对3m =的情况进行了进一步的列举和研究,如下表:n (10)s (3)n (3)s16 8 121 02232 4 1012 0011 1 1 1 1 17 11 122 102 33 7 1020 0021 2 2 2 2 18 14 200 112 34 10 1021 0101 3 2 10 02 19 17 201 122 35 13 1022 0111 4 1 11 01 20 20 202 202 36 16 1100 0121 5 4 12 11 21 2 210 002 37 19 1101 0201 6 1 20 01 22 5 211 012 38 22 1102 0211 7 4 21 11 23 8 212 022 39 25 1110 0221 8 7 22 21 24 11 220 102 40 28 1111 1001 9 1 100 001 25 14 221 112 41 31 1112 1011 10 4 101 011 26 17 222 122 42 34 1120 1021 11 7 102 021 27 20 1000 0202 43 37 1121 1101 12 10 110 101 28 23 1001 0212 44 40 1122 1111 13 13 111 111 29 26 1002 0222 45 43 1200 1121 14 2 112 002 30 29 1010 1002 46 46 1201 1201 155 120 01231 1 1011 000147 2 1202 0002很遗憾,并没有出现如二进制表中一样令人兴奋的现象,经过反复比对和解答,最终得到的规律与代码算法中1.2或是1.3的简化也是异曲同工,其解法最终也归结于批量倒推(详见百度百科词条“约瑟夫问题”中最后一部分),并没有想象中的独特结论.2.3对3m =情况的变题对于2.2尝试的失败,是否意味着之前的进制论不具有实际的意义?我认为答案是否定的. 对此,我再次回顾了题目的描述,并将原题中的留1,2抽杀3改为留1抽杀2,3. 对这一变题在3进制的情况下进行进一步探究,发现了如下表格中所示的规律:n '(10)s (3)n '(3)s (3)s'(3)n -1 1 1 1 0 3 1 10 012 5 4 12 11 1 7 7 14 21 0 9 1 100 001 22 11 4 102 011 21 13 7 111 021 20 15 10 120 101 12 17 13 122 111 11 19 16 124 121 10 21 19 140 201 2 23 22 142 211 1 25 25 144 221 0 27 1 1000 0001 222 29 4 1002 0011 221 31 7 1011 0021 220 33 10 1020 0101 212 35 13 1022 0111 211 37 16 1101 0121 210 39 19 1110 0201 202 41 22 1112 0211 201 43 25 1121 0221 200 45 28 1200 1001 122 4731 1202 1011 121注:为了方便辨识,原本在三进制中没有的数字4在此被用来(不进位留下的数码)进行(3)n 与'(3)s 的比对.在这张表格中我们可以轻易发现同样存在着首位数码减去的1移至末位,并有中间数码平移的情况,与前面不同的是,这次的平移是缩倍平移,首位减1后的数码被除2之后成为了'(3)s 中的前数位. “进制平移外称塔”的规律依然存在. 在证明这一部分的时候我发现,其证明方式与2.1中递推证明2m =的方法方式并无不同,在此就不多作赘述.那么这样一个看起来“漂亮”的结论有什么实际意义呢?实际上,在编程题中来自于定义整型或数组的限制,使得n 具有一定的上限,而在这个方法中我们依靠在线进制转换器和电脑自带的计算器,可以对极大的数码进行运算如123456707654321:(10(3))121012010100100020001123456707622222020521243=(3)(3)21012010100100020001222220202210121001200011121112111110101÷=(3)(10)10121001200011121112111110101182239495434158=其中第二部三进制除法若不熟悉进制除法也可转化为十进制计算,本方法需要如下两个必备条件:进制转化器与科学型计算器.3.引申的整数进制问题的探讨经过了上面的讨论,我们已经对进制法在解决一些实际问题中的应用有了初步的认识,而事实上,进制法在多重削减类问题、整除问题、立方体积问题以及密码问题有大量的运用,版面有限,下述我们将只就其中的几个方面的例题进行分析,加深对进制法解题的印象.3.1在多重削减类问题中的应用其实约瑟夫问题,可以看作是一种多重循环的削减,直至削减到所需的数值为止,故而在这一类题目中,往往根据削减的层级p,整数的p进制有强大的效力,例如称重问题,是反复出现在小、初、高竞赛题中变化不竭的问题之一. 我为以上的这类或与之相似的题目一并命名为“多重削减类问题”,佐证此,特意寻找了记忆犹新的一道来自奥赛经典书本上的题(图片来自网络的扫描版):3.2在整除问题中的应用由于p 进制的独特表述和适应性的计算法则,在整除问题中整数的p 进制通常有很好的实际效用,在接下来的两道题中我将展示第一道题的解答,另一题由于时间紧迫而没有能够解出答案,望老师谅解……例2.1:(2005年中国奥林匹克协作体夏令营试题)如果一个正整数n 在三进制下表示的各数字之和可以被3整除,那么我们称n 为“好的”,则前2005个“好的”正整数之和是多少?解:首先考虑“好的”非负整数,考察如下两个引理:引理1:在3个连续非负整数3,31,32n n n ++(n 是非负整数)中,有且仅有1个是“好的”.证明:在这三个非负整数的三进制表示中,0,1,2各在最后一位出现一次,其作各位数字相同,于是三个数各位数字之和是三个连续的正整数,其中有且仅有一个能被3整除(即“好的”),引理1得证.引理2:在9个连续非负整数9,91,98n n n ++ (n 是非负整数)中,有且仅有3个是“好的”. 把这3个“好的”非负整数化成三进制,0,1,2恰好在这三个三进制数的最后一位各出现一次.证明:由引理1不难得知在9个连续非负整数9,91,98n n n ++ (n 是非负整数)中,有且仅有3个是“好的”.另一方面,在这三个“好的”非负整数的三进制表示中,最高位与倒数第三位完全相同,倒数第二位分别取0,1,2. 若它使它们成为“好的”非负整数,则最后一位不相同,引理2得证.将所有“好的”非负整数按从小到大的顺序排成一列,设第2004个“好的”非负整数为m ,根据引理1,得2003320043m ⨯≤<⨯,即60096012m ≤<.设前m 个“好的”正整数之和为m S ,由于前2003个“好的”正整数之和等于前2004个“好的”非负整数之和. 因此:2003(0122003)320046023022S =+++⨯+= 又因为103(6013)(22020201)=和103(6015)(22020210)=都是“好的”正整数. 因此前2005年“好的”正整数之和是:20052003601360156035050S S =++=例2.2:(2005年国家队数学培训题)互素正整数n p ,n q 满足1111,23n n p q n=+++ 试找出所有的正整数n ,使得3n p .注:也希望老师给这一道题一些点拨和建议.3.3在其他问题中的应用除了上述的典型问题之外,整数p 进制在很多方面仍然有着应用,在此不多作阐述,仅举一例,这一例来自于高中期间我组织全校知识风尚大赛时为高一年级原创的一道笔试思考题(较为简单):例3.1【原创】现甲乙都有一本海门年鉴共1012页(包含绝大多数常用字),一页至多15段,每段至多31行,每行至多31字,而乙的电脑损坏只能识别7或以下的数字. 求设计一种八位密码方式,使两人能正常交流而不持有揭秘方式和密码本年鉴的人不能破译. (4’)(答案也许不唯一,欢迎同学们提供其他方案)解:考虑到1012接近1024,15、31又明显提示进行二进制转化。

约瑟夫问题(算法与数据结构课设报告)

约瑟夫问题(算法与数据结构课设报告)

线性表的操作及其应用一、问题描述线性表、队列是一种常用的数据结构,有顺序和链式两种存储结构,在实际中应用十分广泛,而链表又分为单链表和循环链表,队列又分为链式队列和循环队列。

这些数据结构都可用来解决约瑟夫环问题。

约瑟夫环问题是算法设计中的一个经典问题,是顺序编号的一组n个人围坐一圈,从第1个人按一定方向顺序报数,在报到m时该人出列,然后按相同方法继续报数,直到所有人出列。

设计算法求约瑟夫环中人员的出列顺序。

二、基本要求1、选择合适的存储结构,建立线性表;2、利用顺序存储结构求解约瑟夫环问题;3、利用单链表和循环链表分别求解约瑟夫环问题;4、利用队列求解约瑟夫环问题。

三、测试数据约瑟夫环的测试数据为7,报数为1至3。

四、算法思想由于用到四种不同的存储结构,它们的算法思想依次是:1、首先建立一个顺序表模拟整个约瑟夫环,手动输入顺序表长(即参加约瑟夫循环的人数)和循环的次数和表元素。

用已经输出总人数和顺序表长作比较,作为外层循环条件。

并对每一个输出后的元素重新赋值以为标记。

对于每次循环,首先检查顺序表此次是不是我们设立的标记,如果不是则循环次数加1,当达到要求的循环次数时就将循环次数设置为0,输出该元素到屏幕并将总输出元素加1。

每次外循环我们都会移到表的下一个位置,作为新的判断条件,每次报到表尾的时候,我们都将重新设置到表尾,作为下次循环的表元素。

2、首先采用链式循环链表建立整个约瑟夫环,手动输入第一次的循环次数和每个人所持下一个循环次数。

设立判断指针指向表头,并将该指针是否为空作为外层循环条件。

做一个内层循环,将判断指针移动到循环要输出的数,并设立一个前指针指向该指针的前一个位置,输出该元素后,将循环次数重新赋值成该元素。

接着判断前指针和判断指针比较,如果相等说明整个表已经输出完毕,否则将删除该位置的元素。

3、用链式队列建立循环约瑟夫环,手动输入人数,第一次的循环次数和每个人所持下一个循环次数。

并将每一个元素依次入队列,根据第一次循环次数,建立一个for循环,每一次循环都出队列,如果达到要求的循环次数就输出,否则进队列,这样这个数字就出现在队尾。

约瑟夫环问题实验报告

约瑟夫环问题实验报告

实验报告:约瑟夫环问题1.问题描述:约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

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

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

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

2.实验目的:掌握链表的基本操作:插入、删除、查找等运算,能够灵活应用链表这种数据结构。

3.源程序代码:#include<stdio.h>#include<stdlib.h>typedef struct LNode{int order;int key;LNode *next;}*Linklist;Linklist putlist(){Linklist L,p;int n,i;p=L;printf("输入人的个数");scanf("%d",&n);for(i=1;i<=n;i++){p->next=(Linklist)malloc(sizeof(LNode));p=p->next;p->order=i;scanf("%d",&p->order);p->next=L;}return L;}void outorder(Linklist LG){Linklist p=LG,q=p->next;int i,m;printf("输入m:");scanf("%d",m);while(LG->next!=LG){i=1;while(i<m){p=q;q=q->next;if(q!=LG) i++;};printf("%d",p->order);m=q->key;p->next=q->next;free(q);q=p->next;if(q==LG){p=q;q=q->next;};};}void main(){Linklist L;L=putlist();outorder(L);}4测试数据m=20n=7密码次序:3,1,7,7,4,8,4 输出次序:6,1,4,7,2,3,5。

josephus问题数学解法

josephus问题数学解法

josephus问题数学解法Josephus 问题是一个经典的数学问题,描述如下:有 n 个人围成一圈,从第一个人开始往后报数,报到 m 的人出圈,后面的人继续从 1 开始报数,直到最后一个人留下来。

问最后留下的人的编号是多少?下面是 Josephus 问题的数学解法:设最后留下的人的编号为 f(n,m),则可以得出以下递推式:f(1,m) = 0f(n,m) = (f(n-1,m) + m) % n其中,% 表示取模运算。

以上递推式基于以下思路:假设有 n 个人,编号分别为 0,1,2,...,n-1。

第一轮中,第 m 个人出圈,剩下的人编号为 0,1,...,m-2,m,...,n-1。

由于是围成一圈,所以下一轮中第一个人的编号为 m%n。

而在上一轮中,编号为 m%n 的人出圈了,所以编号为 m%n+1,...,n-1,0,1,...,m%n-1的人组成了一个新的圈。

我们需要求出这个新圈中最后留下的人的编号,即 f(n-1,m)。

由于新圈中每个人的编号都比原来的编号大 m,所以我们需要将 f(n-1,m) 加上 m,以得到在原圈中他的编号。

然而,如果加上 m 后编号超过了 n-1,那么我们需要将编号重新回到 0,这就是将加和结果取模的原因。

最终,当 n=1 时,无论 m 的值为多少,都只剩下编号为 0 的人。

下面是一个 Python 实现的例子:```def josephus(n, m):f = 0for i in range(2, n+1):f = (f + m) % ireturn fprint(josephus(5, 3)) # 输出 3```。

约瑟夫环问题的两种解法(循环链表和公式法)

约瑟夫环问题的两种解法(循环链表和公式法)

约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述: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),显然,这不是⼀个好的算法。

约瑟夫问题 完整C程序代码

约瑟夫问题     完整C程序代码

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。

完整代码:#include<stdio.h>#include<stdlib.h>struct person{int num;int order;struct person *next;};static struct person *head=NULL;struct person *CreatList(void){struct person *rear;struct person *p;int k=0;while(1){p=(struct person*)malloc(sizeof(struct person));p->order=++k;printf("\n请输入一个人所持的密码,输入0则建表结束:");scanf("%d",&p->num);if(p->num==0)break;if(head==NULL) head=p;else rear->next=p;rear=p;}if(rear!=NULL) rear->next=head;printf("\n建表结束\n");return head;}void josephus(struct person *p,int m) {int i,k;struct person* r;if(m==1)p=p->next;else{for(i=2;i<m;i++)p=p->next;}r=p->next;k=r->num;printf("%d ",k);printf("%d\n",r->order);p->next=r->next;free(r);if(p!=p->next)josephus(p->next,k);else{printf("%d ",p->num);printf("%d\n",p->order);}}void main(){int m;struct person *pos;CreatList();printf("请输入初始值m:");scanf("%d",&m);printf("密码顺序\n");pos=head;josephus(pos,m);}/* 测试数据,复制粘贴可用3172 4 8 4 0 20 */。

循环队列解决约瑟夫问题

循环队列解决约瑟夫问题

循环队列解决约瑟夫问题有n个⼈围成⼀圈,从第1个⼈开始,1,2,…,m报数,报⾄m出局,余下的⼈继续从1,2,…,m报数,重复之前的流程,要求:求出被淘汰编号的序列,及最后剩下的⼀⼈是原来的第⼏号?(要求:⽤循环队列解决该问题。

)#ifndef STATUS_H#define STATUS_H#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2typedef int Status;#endif#include <iostream>using namespace std;#include "Status.h"typedef int ElemType;typedef struct{ElemType *base;int front;int rear;int MAXSIZE;}SqQueue;Status InitQueue(SqQueue& Q,int n) //初始化队列{Q.base = new ElemType[100];if(!Q.base){cout << "创建队列失败!";return ERROR;}Q.front=Q.rear=0;Q.MAXSIZE = n+1;//MAXSIZE是总⼈数+1,是为了留出⼀个空位置来放置rearreturn OK;}void QueueTraverse(SqQueue Q) //遍历队列{int i;i=Q.front;while(i!=Q.rear){cout<<Q.base[i]<<"";i=(i+1)%Q.MAXSIZE;}cout<<endl;}Status EnQueue(SqQueue& Q,ElemType e) //⼊队{if((Q.rear+1)%Q.MAXSIZE==Q.front){cout << "队列已满!";return ERROR;}Q.base[Q.rear] = e;Q.rear = (Q.rear+1)%Q.MAXSIZE;return OK;}Status DeQueue(SqQueue& Q,ElemType& e) //出队{if(Q.front==Q.rear){cout << "队列为空!";return ERROR;}e = Q.base[Q.front];Q.base[Q.front] = 0;Q.front = (Q.front+1)%(Q.MAXSIZE-1);//总⼈数为MAXSIZE-1return OK;}int main(){int n,m,i=1;SqQueue Q;ElemType e;cout << "请输⼊n个⼈(n<=100):";do{cin >> n;if(n>100 || n<1){cout << "输⼊⼈数错误!请重新输⼊:";}}while(n>100 || n<1);InitQueue(Q,n);while(i<=n)//⼊队操作{EnQueue(Q,i);i++;}cout << "\n此时的序列顺序为:"<<endl;QueueTraverse(Q);cout << "\n请输⼊第m个⼈出队(1<=m<=n):";do{cin >> m;if(m>n || m<1){cout << "m输⼊错误!请重新输⼊:";}}while(m>n || m<1);cout << endl;int Count = n;//⽤来记录剩下的⼈数while(Count != 1){i = 1;//i⽤来控制是第⼏个⼈报数while(i != m)//当i的值不等于m的值时{Q.front = (Q.front+1)%(Q.MAXSIZE-1);//总⼈数为MAXSIZE-1if(Q.base[Q.front] != 0)//当此时不为0的话,i++⽤来控制第⼏个⼈{i++;}}DeQueue(Q,e);while(Q.base[Q.front] == 0)//当此时为0的时候,循环找到下⼀个不为0的位置{Q.front = (Q.front+1)%(Q.MAXSIZE-1);}cout << "序号:" << e << "出局!\n";Count--;}DeQueue(Q,e);cout << "\n最后⼀个是:" << e << endl;return0;}1.有n个⼈围成⼀圈,从第1个⼈开始,1,2,…,m报数,报⾄m出局,余下的⼈继续从1,2,…,m报数,重复之前的流程,要求:求出被淘汰编号的序列,及最后剩下的⼀⼈是原来的第⼏号?(要求:⽤循环队列解决该问题。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
m=m->next;}
if(m->next==m)
printf("编号为%d的人出列\n",m->data);
return 0;
}
printf("请输入报数开始人序号:");
scanf("%d",&x-1;i++)
{q=(LinkList)malloc(sizeof(LNode));
r->data=i;
r->next=q;
r=q;}//创建x个单链表
q->data=x;
{int x,y,z;
LinkList p,q,r;//p用来指向第一个人,r、q用来实现尾插法构建链表
p=(LinkList)malloc(sizeof(LNode));
printf("请输入总人数:");
scanf("%d",&x);
printf("请输入报数大小:");
scanf("%d",&y);
#include <stdio.h>
#include <stdlib.h>
typedef struct node{
int data;
struct node *next;
}LNode,*LinkList;
int YSF(LinkList m,int n,int l);
void main()
{while(m->next!=m)
{for(int k=1;k<n-1;k++)
{m=m->next;}
int s=1;
if(l==s){
printf("编号为%d的人出列\n",m->next->data);
break;}
else {n++;}
m->next=m->next->next;//剔除满足报出y人的结点
q->next=p;//构成循环链表
for(int j=1;j<=z-1;j++)
{p=p->next;}//找到开始报数人
printf("请输入序号:");
int o;
scanf("%d",&o);
YSF(p,y,o);}
int YSF(LinkList m,int n,int l)
相关文档
最新文档