约瑟夫问题
- 格式:doc
- 大小:36.50 KB
- 文档页数:5
“约瑟夫”问题及若干变种例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),对于极限数据会超时。
约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。
在计算机编程的算法中,类似问题又称为约瑟夫环。
又称“丢手绢问题”)据说著名犹太历史学家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);/*进行约瑟夫操作*/。
“约瑟夫”问题及若干变种林厚从例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课程设计约瑟夫问题一、教学目标本章节的教学目标分为三个维度:知识目标、技能目标和情感态度价值观目标。
1.知识目标:通过学习约瑟夫问题,学生能够理解并掌握约瑟夫问题的基本概念、原理和解决方法。
2.技能目标:学生能够运用所学的约瑟夫问题的解决方法,解决实际问题,并能够进行简单的数学推理和分析。
3.情感态度价值观目标:通过学习约瑟夫问题,学生能够培养对数学的兴趣和好奇心,提高解决问题的能力和团队合作的精神。
二、教学内容本章节的教学内容以约瑟夫问题为核心,包括以下几个方面:1.约瑟夫问题的定义和背景介绍。
2.约瑟夫问题的解决方法,包括递归法、循环队列法和迭代法。
3.约瑟夫问题的应用场景,结合实际问题进行讲解。
4.约瑟夫问题的扩展,如约瑟夫环的生成和约瑟夫问题的变种。
三、教学方法为了激发学生的学习兴趣和主动性,本章节将采用多种教学方法:1.讲授法:通过讲解约瑟夫问题的定义、原理和解决方法,使学生掌握基本概念和知识点。
2.案例分析法:通过分析实际问题,引导学生运用所学的约瑟夫问题的解决方法,培养学生的解决问题的能力。
3.实验法:通过进行约瑟夫问题的编程实验,让学生亲手实践,加深对解决方法的理解和记忆。
四、教学资源为了支持教学内容和教学方法的实施,丰富学生的学习体验,将选择和准备以下教学资源:1.教材:选用权威、系统的教材,如《计算机科学导论》等,作为学生学习的基本依据。
2.参考书:推荐一些相关的参考书,如《算法导论》等,供学生进一步拓展学习。
3.多媒体资料:制作PPT、教学视频等多媒体资料,以图文并茂的形式呈现教学内容,提高学生的学习兴趣。
4.实验设备:准备计算机实验室,让学生能够进行约瑟夫问题的编程实验,提高实践能力。
五、教学评估本章节的评估方式包括平时表现、作业和考试三个部分,以全面、客观、公正地评估学生的学习成果。
1.平时表现:通过观察学生在课堂上的参与度、提问和回答问题的表现,了解学生的学习态度和理解程度。
【问题】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.。