约瑟夫问题

合集下载

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

算法设计
Josephus jp=new Josephus(); int a[]=new int[n]; for(int i=0;i<n;i++){ a[i]=i+1; } jp.SortArray(a,n,m,k,g); } public void show(int[]b,int g){ for(int i=b.length-g;i<b.length;i++){ System.out.print(b[i]+" "); } }
• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 48 6 15 47 21 46 105 73 4 87 32 21 300 80 12 70 296 198 总人数n 起始号码k 循环数m 68 34 25
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。

3约瑟夫斯问题

3约瑟夫斯问题

后顺时针每隔1枚拿走2枚棋子,连续 转了10周,9次越过A.当将要第10次越 过A处棋子取走其它棋子时,小洪发 现圆周上余下20多枚棋子. 若N是14的 倍数,则圆周上还有多少枚棋子?
作业:
在一个圆周上放一枚黑色 的和1990枚白色棋子.一个 同学进行这样的操作:从 黑色开始,按顺时针方向, 每隔1枚,取走1枚.当他取 到黑子时,圆周上还剩下 多少枚棋子?
• 例2:有1000个学生坐成一圈,依次编号为1,2, 3,…,1000. 现在进行1,2,1,2,…这样报数. 1号学生报1后立即离开,2号学生报2并留下; 三号学生报1后立即离开,4号学生报2并留下; …;如此进行下去,报1的同学离开,报2的同 学留下,直到最后还剩下1个人. 问这个人的编 号是多少?
约瑟夫斯问题
• 在古代,有一个野蛮部落,将抓起来的俘 虏围成一圈,并从某人开始按顺时针方向 以自然数1,2,3, 编号,然后杀掉1号、 3号、5号如此进行下去,隔一个人杀一个 人,直至最后余下一个人为止,这个幸存 者就是约瑟夫斯.
• 例1:有1000人排成一排,依次编号为1,2,3, …,1000. 现在进行1,2,1,2,…这样报数. 1 号学生报1后立即离开,2号学生报2并留下;3号 学生报1后立即离开,4号学生报2并留下;…; 999号学生报1后立即离开,1000号学生报2并留 下;然后从2号开始重新报数,2号学生报1后立 即离开,4号学生报2并留下;…;如此进行下去 ,报1的同学离开,报2的同学留下,直到最后还 剩下1个人. 问这个人的编号是多少?
• 例3:有100张的一摞卡片,玲玲拿着它们,从 最上面的一张开始按如下的顺序进行操作:把 最上面的第一张卡片舍去,把下一张卡片放在 这一摞卡片的最下面。再把原来的第三张卡片 舍去,把下一张卡片放在最下面. 反复这样做直 到手中只剩下一张卡片,那么剩下的这张卡片 是原来那一摞卡片的第几张?

约瑟夫问题及变种

约瑟夫问题及变种

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

约瑟夫问题多种解决方法

约瑟夫问题多种解决方法

• • • • • • • • • • •
s:=0; while s<n do begin if j<m then inc(j) else j:=1; s:=s+a[j]; end; write(j); a[j]:=0; end; end.

约瑟夫问题多 种解决方法
约瑟夫问题的来历
• 据说著名犹太历史学家 Josephus有过以下的故事:在罗 马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋 友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓 到,于是决定了一个自杀方式,41个人排成一个圆圈,由 第1个人开始报数,每报数到第3人该人就必须自杀,然后 再由下一个重新报数,直到所有人都自杀身亡为止。然而 Josephus 和他的朋友并不想遵从,Josephus要他的朋友 先假装遵从,他将朋友与自己安排在第16个与第31个位置, 于是逃过了这场死亡游戏。17世纪的法国数学家加斯帕在 《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余 的人才能幸免于难,于是想了一个办法:30个人围成一圆 圈,从第一个人开始依次报数,每数到第九个人就将他扔 入大海,如此循环进行直到仅余15个人为止。问怎样排法, 才能使每次投入大海的都是非教徒。
著名约瑟夫问题一
• 17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了 这样一个故事:15个教徒和15 个非教徒在深海上遇险, 必须将一半的人投入海中,其余的人才能幸免于难,于是 想了一个办法:30个人围成一圆圈,从第一个人开始依次 报数,每数到第九个人就将他扔入大海,如此循环进行直 到仅余15个人为止。问怎样排法,才能使每次投入大海的 都是非教徒。题目中30个人围成一圈,因而启发我们用一 个循环的链来表示。可以使用结构数组来构成一个循环链。 结构中有两个成员,其一为指向下一个人的指针,以构成 环形的链;其二为该人是否被扔下海的标记,为1表示还 在船上。从第一个人开始对还未扔下海的人进行计数,每 数到9时,将结构中的标记改为0,表示该人已被扔下海了。 这样循环计数直到有15个人被扔下海为止

约瑟夫问题pascal

约瑟夫问题pascal

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

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

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

然而Josephus 和他的朋友并不想遵从。

首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。

接着,再越过k-1个人,并杀掉第k个人。

这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

//详情请见百度百科。

[题目描述]Josephus问题是建立在历史学家Josephus的一个报告的基础之上,该报告讲述了他和40个士兵在公元67年被罗马军队包围期间签定的一个自杀协定,Josephus建议每个人杀死他的邻居,他很聪明的使自己成为这些人中的最后一个,因此获得生还。

21世纪的今天,我们将Josephus问题稍作扩展:设N个人围成一圈,依次从1到N编号,从第S号开始报数,报到K的人出列,求第T个出列的人的编号。

显然,Josephus面对的是N = 40, K = 2, S= 1, T = 40的退化情况。

[数据范围]30%的数据,K = 2100%的数据,1 <= N <= 1000000, 1<= K <= 2147483647, 1 <= S <= N, 1 <= T <= N最简单的推导方法应该是模拟。

实验一、约瑟夫问题

实验一、约瑟夫问题

实验一:约瑟夫问题求解一、问题描述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,则不足四位);

约瑟夫问题大全

约瑟夫问题大全

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

c课程设计约瑟夫问题

c课程设计约瑟夫问题

c课程设计约瑟夫问题一、教学目标本章节的教学目标分为三个维度:知识目标、技能目标和情感态度价值观目标。

1.知识目标:通过学习约瑟夫问题,学生能够理解并掌握约瑟夫问题的基本概念、原理和解决方法。

2.技能目标:学生能够运用所学的约瑟夫问题的解决方法,解决实际问题,并能够进行简单的数学推理和分析。

3.情感态度价值观目标:通过学习约瑟夫问题,学生能够培养对数学的兴趣和好奇心,提高解决问题的能力和团队合作的精神。

二、教学内容本章节的教学内容以约瑟夫问题为核心,包括以下几个方面:1.约瑟夫问题的定义和背景介绍。

2.约瑟夫问题的解决方法,包括递归法、循环队列法和迭代法。

3.约瑟夫问题的应用场景,结合实际问题进行讲解。

4.约瑟夫问题的扩展,如约瑟夫环的生成和约瑟夫问题的变种。

三、教学方法为了激发学生的学习兴趣和主动性,本章节将采用多种教学方法:1.讲授法:通过讲解约瑟夫问题的定义、原理和解决方法,使学生掌握基本概念和知识点。

2.案例分析法:通过分析实际问题,引导学生运用所学的约瑟夫问题的解决方法,培养学生的解决问题的能力。

3.实验法:通过进行约瑟夫问题的编程实验,让学生亲手实践,加深对解决方法的理解和记忆。

四、教学资源为了支持教学内容和教学方法的实施,丰富学生的学习体验,将选择和准备以下教学资源:1.教材:选用权威、系统的教材,如《计算机科学导论》等,作为学生学习的基本依据。

2.参考书:推荐一些相关的参考书,如《算法导论》等,供学生进一步拓展学习。

3.多媒体资料:制作PPT、教学视频等多媒体资料,以图文并茂的形式呈现教学内容,提高学生的学习兴趣。

4.实验设备:准备计算机实验室,让学生能够进行约瑟夫问题的编程实验,提高实践能力。

五、教学评估本章节的评估方式包括平时表现、作业和考试三个部分,以全面、客观、公正地评估学生的学习成果。

1.平时表现:通过观察学生在课堂上的参与度、提问和回答问题的表现,了解学生的学习态度和理解程度。

约瑟夫问题

约瑟夫问题




第一个人出列:从 数器 j 从 1 数到 person[i] <- 0 第二个人出列:从 数器 j 从 1 数到 person[i] <- 0 „„ n-1个出列:„„
k = 0 的后继开始扫描数组,报数计 m,该人出列,把他的状态置成 0,
k = i 的后继开始扫描数组,报数计 m,该人出列,把他的状态置成 0,
此题如果意思是定位第 m 个元素,当不需要跳过任何元素 时。 从 k 开始报数第 m 个元素是: ((( k + m - 1 ) – 1 ) mod ) n +1
“遍历”和“数到”的区别: 遍历到的每个元素需要做相同的操作,而“数到m”第m个 元素和前 m-1 个元素有不同的操作。
如果需要跳过某些标记的元素,就无法直接确定第 m 个元 素的位置,则需要按顺序逐个扫描元素,跳过不符合要求的 元素,对符合要求的元素进行计数。 此处注意第一个数也许需要跳过,第一个报数的编号也许不 是 k,不能直接从 1 计数。 i <- k j <- 0 while ( j< m ) do begin i <- (i mod n) + 1 if ( person[i] <> 0 ) then j <- j+1 end

2.1
模拟方法
2.2
2.1.1 数组+标记 2.1.2 循环链表+删除节点
数学模型



定义数组: array person[1..n]代表 n 个人的状态; 第i个元素代表编号为i的人的状态,下标代表人的编号; 状态: 1代表没有出列,0代表已出列。 使用线性数组表示环状圆圈 * 数组元素间相邻关系和环状结构元素间相邻关系有所不 同,person[n]没有后继节点,person[1]没有前驱节点。 数组可以使用下面方法实现环状结构的相邻关系: person[i]后继节点的下标为j: if i = n then i<- 1 else i <- i+1 或者 i <- (i mod n) +1

约瑟夫斯问题

约瑟夫斯问题

约瑟夫斯问题约瑟夫问题维基百科,⾃由的百科全书跳到导航跳到搜索约瑟夫问题(有时也称为约瑟夫斯置换),是⼀个出现在计算机科学和数学中的问题。

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

⼈们站在⼀个等待被处决的圈⼦⾥。

计数从圆圈中的指定点开始,并沿指定⽅向围绕圆圈进⾏。

在跳过指定数量的⼈之后,处刑下⼀个⼈。

对剩下的⼈重复该过程,从下⼀个⼈开始,朝同⼀⽅向跳过相同数量的⼈,直到只剩下⼀个⼈,并被释放。

问题即,给定⼈数、起点、⽅向和要跳过的数字,选择初始圆圈中的位置以避免被处决。

历史这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的⼀名犹太历史学家。

他在⾃⼰的⽇记中写道,他和他的40个战友被罗马军队包围在洞中。

他们讨论是⾃杀还是被俘,最终决定⾃杀,并以抽签的⽅式决定谁杀掉谁。

约瑟夫斯和另外⼀个⼈是最后两个留下的⼈。

约瑟夫斯说服了那个⼈,他们将向罗马军队投降,不再⾃杀。

约瑟夫斯把他的存活归因于运⽓或天意,他不知道是哪⼀个。

[1]解法⽐较简单的做法是⽤循环单链表模拟整个过程,时间复杂度是O(n*m)。

如果只是想求得最后剩下的⼈,则可以⽤数学推导的⽅式得出公式。

且先看看模拟过程的解法。

Python版本-- coding: utf-8 --class Node(object):def init(self, value):self.value = valueself.next = Nonedef create_linkList(n):head = Node(1)pre = headfor i in range(2, n+1):newNode = Node(i)pre.next= newNodepre = newNodepre.next = headreturn headn = 5 #总的个数m = 2 #数的数⽬if m == 1: #如果是1的话,特殊处理,直接输出print (n)else:head = create_linkList(n)pre = Nonecur = headwhile cur.next != cur: #终⽌条件是节点的下⼀个节点指向本⾝for i in range(m-1):pre = curcur = cur.nextprint (cur.value)pre.next = cur.nextcur.next = Nonecur = pre.nextprint (cur.value)using namespace std;typedef struct _LinkNode {int value;struct _LinkNode* next;} LinkNode, *LinkNodePtr;LinkNodePtr createCycle(int total) {int index = 1;LinkNodePtr head = NULL, curr = NULL, prev = NULL;head = (LinkNodePtr) malloc(sizeof(LinkNode));head->value = index;prev = head;while (--total > 0) {curr = (LinkNodePtr) malloc(sizeof(LinkNode));curr->value = ++index;prev->next = curr;prev = curr;}curr->next = head;return head;}void run(int total, int tag) {LinkNodePtr node = createCycle(total);LinkNodePtr prev = NULL;int start = 1;int index = start;while (node && node->next) {if (index == tag) {printf("%d\n", node->value);prev = node->next;node->next = NULL;node = prev;} else {prev->next = node->next;node->next = NULL;node = prev->next;}index = start;} else {prev = node;node = node->next;index++;}}}int main() {if (argc < 3) return -1;run(atoi(argv[1]), atoi(argv[2]));return 0;}数学推导解法我们将明确解出{\displaystyle k=2}k=2时的问题。

约瑟夫问题

约瑟夫问题

约瑟夫问题简介约瑟夫问题是一个经典的数学问题,由弗拉维奥·约瑟夫斯(Josephus Flavius)所提出。

问题的背景是:在一个固定长度的圆桌周围围坐着n个人,从第一个人开始报数,报到m的人出圈,然后从下一个人开始重新报数,直到剩下最后一个人。

问题是,给定n和m,求最后剩下的人的编号。

解法暴力破解法最直观的解法是使用循环和数组来模拟这个过程。

首先,我们用一个数组来表示这些人的编号,例如[1, 2, 3, ..., n]。

然后我们在循环中模拟报数的过程,每次报到m的人就从数组中删除。

当数组中只剩下一个元素时,就找到了最后剩下的人。

def josephus(n, m):people = list(range(1, n +1))idx =0while len(people) >1:idx = (idx + m -1) % len(people)people.pop(idx)return people[0]这种解法的时间复杂度为O(n*m),并且在n很大的情况下,性能会变得很差。

数学公式法约瑟夫问题其实存在一个更巧妙的解法,不需要模拟整个过程,而是通过数学公式来直接计算出最后剩下的人的编号。

如果我们将问题的规模缩小为n-1,即在一个长度为n-1的圆桌上进行同样的操作,求出的结果为f(n-1, m)。

然后我们将这个结果映射到原始问题的编号上,即将f(n-1, m)变为f(n, m)。

计算f(n, m)的过程如下: - 首先,我们将f(n, m)映射到f(n-1, m)上。

假设f(n, m) = x,那么f(n, m)在映射到f(n-1, m)上的过程中,每次都将整个序列向后移动m 位,即f(n, m)在映射到f(n-1, m)上的过程中,原来的第x个元素变成了第x+m个元素。

因此,f(n-1, m) = (x + m) % n。

- 其次,我们将f(n-1, m)映射回f(n, m)上。

约瑟夫问题多种解决方法

约瑟夫问题多种解决方法
1
著名约瑟夫问题一
• 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<<"----------------------------------------

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
问题描述
• 已知n(<2^15)个人(以编号1,2,…,n 分别表示)围坐在一圆桌上,从编号为k (1≤ k≤ n)的人开始报数,数到m的那个 人出列,他的下一个人又从1开始报数,数 到m的那个人又出列,依此重复,直到圆桌 周围的人全部出列,依次输出最后个整数T(<2^15)表示测 试次数,接着第二到T+1行分别为n,m和k 的值。 例:2 10 2 3
• public void SortArray(int[]a,int n,int m,int k,int g){ • int[] b=new int[n]; • int c=0; • int i=k-2; • while(true){ • for(int j=0;j<m;){ • i=(i+1)%n; • if(a[i]!=0){ • j++; • } • }
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 5 2 2 2 1 4 20 6
数据测试
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人
10 2 3 6 1 5 30 4 7 18 23 26
循环数m
最后出列的3人
8
3 14 6
数据测试
最后出列的3人 总人数n

约瑟夫问题

约瑟夫问题

约瑟夫问题约瑟夫问题约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。

例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。

最后剩下1号。

假定在圈子里前K个为好人,后K个为坏人,你的任务是确定这样的最少M,使得所有的坏人在第一个好人之前被杀掉。

举个例子:有64名战士被敌人俘虏了。

敌人命令他们拍成一圆圈,编上号码1,2,3…,64。

敌人把1号杀了,又把3号杀了,他们隔着一个杀一个这样转着圈杀。

最后只剩下一个人,这个人就是约瑟夫斯。

请问约瑟夫斯是多少号?(这就是“约瑟夫斯”问题。

)这个问题解答起来比较简单:敌人从1号开始,隔一个杀一个,第一圈把所有的奇数号码的战士圈杀光了。

剩下的32名战士需要重新编号,而敌人在第二圈杀死的是重新编号的奇数号码。

由于第一圈剩下的全部是偶数号2,4,6,…,64。

把它们全部用2去除,得1,2,3,…,32。

这是第二圈编的号码。

第二圈杀过以后,又把奇数号码都杀掉了,还剩16个人。

如此下去,可以想到最后剩下的必然是64号。

$64=2^6$,它可以连续被2整除6次,是从1到64中能被2整除次数最多的数,因此,最后必然把64 号留下。

如果有65名战士被俘,敌人还是按上述的方法残杀战士,最后还会剩下约瑟夫斯吗?经过计算,很容易得到结论,不是。

因为第一个人被杀后,也就是1号被杀后,第二个被杀的是必然3号。

如果把1号排除在外,那么还剩下的仍是64人,新1号就是3号。

这样原来的2号就变成了新的64 号,所以剩下的必然是2号。

进一步的归类,不难发现如果原来有$2^k$个人,最后剩下的必然$2^k$号;如果原来有$2^k+1$个人,最后剩下2号;如果原来有$2^k+2$个人,最后剩下4号……如果原来有$2^k+m$个人,最后剩下2m号.比如:原来有100人,由于$100=64+36=2^6+36$,所以最后剩下的就是36×2=72号;又如:原来有11 1人,由于$100=64+47=2^6+47$,所以最后剩下的就是47×2=94号传说古代有一批人被蛮族俘虏了,敌人命令他们排成圆圈,编上号码1,2,3,…然后把1号杀了,把3号杀了,总之每隔一个人杀一个人,最后剩下一个人,这个人就是约瑟夫斯。

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析Josephu问题为:设置编号为1,2,3,......n的n个⼈围坐⼀圈,约定编号为k(1<=k<=n)的⼈从1看是报数,数到m的那个⼈出列,它的下⼀位⼜从1开始报数,数到m的那个⼈出列,以此类推,直到所有⼈出列为⽌,由此产⽣⼀个出队编号的序列。

提⽰:⽤有个不带头的循环链表来处理Josephu问题:先构成⼀个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下⼀个结点⼜从1开始计数,直到最后⼀个结点从链表中删除算法结束。

代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(5);cycLink.createLink();cyclink.show();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;//设置链表⼤⼩public void setLen(int len){this.len=len;}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}优化:代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(50);cycLink.createLink();cycLink.setK(2);cycLink.setM(3);cyclink.show();cyclink.play();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了 Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;int k=0;int m=0;//设置mpublic void setM(int m){this.m=m;}//设置链表⼤⼩public void setLen(int len){this.len=len;}//设置从第⼏个⼈开始数数public void setK(int k){this.k=k;}//开始playpublic void play(){Child temp=this.fistChild;//1.先找到开始数数的⼈//int i=1;i<k;因为⾃⼰也要数⼀下,所以i不能为k for(int i=1;i<k;i++){temp=temp.nexChild;}while(this.len!=1){//2.数m下for(int j=1;j<m;j++){temp=temp.nextChild;}//找到要出圈的前⼀个⼩孩,有待优化Child temp2=temp;while(temp2.nextChild!=temp){temp2=temp2.nextChild;}//3.将数到m的⼩孩,退出圈temp2.nextChild=temp.nextChild;//让temp指向数数的⼩孩temp=temp.nextChild;this.len--;}//最后⼀个⼩孩(验证)System.out.println(temp.no);}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦ temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}。

经典数学故事之约瑟夫问题

经典数学故事之约瑟夫问题

有⼀个古⽼的传说,有64名战⼠被敌⼈俘虏了,敌⼈命令它们排成⼀个圈,编上号码1,2,3,……64。

敌⼈把1号杀了,⼜把3号杀了,他们是隔⼀个杀⼀个这样转着圈杀。

最后剩下⼀个⼈,这个⼈就是约瑟夫,请问约瑟夫是多少号?
这就是数学上有名的“约瑟夫问题”。

给⼤家⼀个提⽰,敌⼈从l号开始,隔⼀个杀⼀个,第⼀圈把奇数号码的战⼠全杀死了。

剩下的32名战⼠需要重新编号,⽽敌⼈在第⼆圈杀死的是重新编排的奇数号码。

按照这个思路,看看你能不能解决这个问题?
答案解析:
由于第⼀圈剩下的全部是偶数号2,4,6,8,……64。

把它们全部⽤2除,得1,2,3,4,……32.这是第⼆圈重新编的号码。

第⼆圈杀过之后,⼜把奇数号码都杀掉了,还剩下16个⼈。

如此下去,可以想到最后剩下的必然是64号。

64=2×2×2×2×2×2,它可以连续被2整除6次,是从1到64中质因数⾥2最多的数,因此,最后必然把64号剩下。


64=2×2×2×2×2×2还可以看到,是转过6圈之后,把约瑟夫斯剩下来的。

最新实验一约瑟夫问题实验报告

最新实验一约瑟夫问题实验报告

最新实验一约瑟夫问题实验报告实验目的:探究约瑟夫问题(Josephus Problem)的数学规律及其在不同参数下的表现,验证相关算法的效率和准确性。

实验背景:约瑟夫问题是一个著名的理论问题,源自于罗马时代的一个传说。

问题可以描述为:n个人围成一圈,从第一个人开始报数,每数到第m个人,该人出圈,然后从下一个人重新开始报数,如此循环,直到所有人出圈。

本实验旨在通过编程模拟这一过程,并分析结果。

实验方法:1. 采用编程语言(如Python)编写约瑟夫问题的模拟程序。

2. 设定不同的n和m值,运行程序,记录每个人的出圈顺序及最后剩下的人的位置。

3. 分析不同n和m值下的出圈顺序规律。

4. 对比不同算法(如递归法、迭代法)的运行时间,评估效率。

实验步骤:1. 初始化参数:确定模拟的总人数n和报数间隔m。

2. 创建一个循环队列模拟人们围成的圈。

3. 通过循环和条件判断模拟报数和出圈过程。

4. 记录每次出圈的人的编号和最终剩下的人的位置。

5. 改变n和m的值,重复步骤1至4,收集多组数据。

6. 分析数据,寻找出圈规律。

7. 对模拟过程进行计时,比较不同算法的运行时间。

实验结果:1. 通过大量实验数据,发现当n和m的值较小时,可以直观看出出圈顺序的规律。

2. 随着n和m值的增大,出圈顺序变得更加复杂,但依然存在一定的规律性。

3. 实验中使用的迭代法在处理大规模数据时,相比递归法具有更高的效率,递归法在深度较大时可能会导致栈溢出。

4. 通过图表展示了不同n和m值下,最后剩下的人的位置的概率分布。

实验结论:1. 约瑟夫问题的出圈顺序并非完全随机,存在一定的数学规律。

2. 迭代法在解决大规模约瑟夫问题时更为高效和稳定。

3. 本实验为进一步研究约瑟夫问题提供了实验数据和算法优化方向。

建议:对于未来的研究,可以尝试将约瑟夫问题推广到更多变种,如双向报数、不同方向报数等,以及探索其在实际问题中的应用,如网络协议设计、资源分配等。

约瑟夫问题数据结构实验报告

约瑟夫问题数据结构实验报告

约瑟夫问题数据结构实验报告[正文]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.约瑟夫问题:亦称为约瑟夫环问题,是一个数学难题,起源于古代历史记载,已有几个世纪的历史。

相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

【问题】n只猴子选大王,选举办法如下:从头到尾1,2,3报数,凡报3的退出,余下的从尾到头1,2,3报数,凡报3的退出...如此类推,当剩下两
只猴子时,取这时报1的为王,若想当猴王,请问当初应占据什么位置?
【测试数据】
n │ 7 │ 10│20│100 │
位置│2 │ 8│16│ 77 │
【参考程序1】
const number=3;
var n,num,i,total:integer;
a:array[1..100]of boolean; order:boolean;
begin
writeln('input n:');
readln(n);
total:=n; {队伍中剩下的猴子数}
for i:=1 to n do a[i]:=true;
repeat
order:=true; {order=true:顺序报数}
num:=0; {num:报的数字}
for i:=1 to n do {顺序报数}
if a[i] then begin {第i只在队列中才有资格报数}
num:=num+1;
if (num=number) then begin {如果为3}
a[i]:=false;total:=total-1; {出队,且猴子数少1}
num:=0; {num置0,又准备从1报起}
end; {报到尾}
if total>number-1 then order:=false; {如还要继续报,则从尾到头}
num:=0; {从尾到头时,order=false}
for i:=n downto 1 do {逆向报数}
if a[i] then begin {在队列中}
num:=num+1;
if (num=number) then begin
a[i]:=false;total:=total-1;
num:=0;
end;
end;
until total=number-1; {直到剩下2只}
if not order then for i:=1 to n do if a[i] then begin
{报剩2只时,如上一次为从尾到头报数,则猴王为从头到尾报的第一只}
writeln('The Monkey King is :',i);readln;halt;end;
if order then for i:=n downto 1 do if a[i] then begin
{报剩2只时,如上一次为从头到尾报数,则猴王为从尾到头报的第一只}
writeln('The Monkey King is :',i);readln;halt;end;
end.
【参考程序2】程序1中,用了order来记录是顺序还是逆序报数,不太方便。

现简化之,取消了order,而在每一次报数前都判断是否只剩2只。

const number=3;
var n,num,i,total:integer; a:array[1..200]of boolean;
writeln('input n:');
readln(n);
total:=n;
for i:=1 to n do a[i]:=true;
repeat
num:=0;
for i:=1 to n do
if a[i] then begin
if total=2 then begin writeln('he is :',i); readln;halt;end; num:=num+1;
if (num=number) then begin
a[i]:=false;total:=total-1;
num:=0;
end;
end;
num:=0;
for i:=n downto 1 do
if a[i] then begin
if total=2 then begin writeln('he is :',i); readln; halt;end; num:=num+1;
if (num=number) then begin
a[i]:=false;total:=total-1;
num:=0;
end;
until total<number-1;
end.
【问题】围绕着山顶有10个洞,狐狸要吃兔子,兔子说:“可以,但必须找到我,我就藏身于这十个洞中,你从10号洞出发,先到1号洞找,第二次隔1个
洞找,第三次隔2个洞找,以后如此类推,次数不限。

”但狐狸从早到晚进
进出出了1000次,仍没有找到兔子。

问兔子究竟藏在哪个洞里?
【答案】2,4,7,9
【参考程序1】【参考程序2】
const const
holenumber=10; m=10; {洞数}
var var
hole:array[0..holenumber] l,f,t:integer;
of 0..1; a:array[1..m+1] of 0..1;
step,i,number:longint; begin
begin for t:=1 to m do
for i:=0 to 9 do hole[i]:=0; a[t]:=0;
number:=0; f:=0; t:=0; l:=0;
for step:=1 to 1000 do begin repeat
number:=number+step; {循环地数} l:=l+1; {隔几个洞}
i:=number mod holenumber; {第几个洞} t:=t+l; {第几个洞}
hole[i]:=1; if t>m+1 then t:=t mod m;
end; if t=0 then t:=10;{循环地找}
for i:=0 to holenumber-1 do a[t]:=1;{该洞已进过}
if hole[i]=0 then write(i:3); f:=f+1; {进洞的次数} readln; until f=1000;
end. for t:=1 to 10 do
if a[t]=0 then write(t,' ');
readln
end.。

相关文档
最新文档