这个是什么叫问题问题

问题(有时也称为约瑟夫斯置换是一个出现在计算机科学和数学中的问题。在计算机编程的算法中类似问题又称为约瑟夫环。又称“丢手绢问题”.)

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

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

个人。这个过程沿着圆圈一直进行直到最终只剩下一个人留下,这个人就可以继续活着问题是,给定了和一开始要站在什么叫问题地方才能避免被处决?Josephus要他的朋友先假装遵从他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏

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

约瑟夫问题并不难但求解的方法很多;题目的变化形式也很多。这里给絀一种实现方法

题目中30个人围成一圈,因而启发我们用一个循环的链来表示可以使用结构

来构成一个循环链。结构中有两个成员其┅为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记为1表示还在船上。从第一个人开始对还未扔下海的人进行計数每数到9时,将结构中的标记改为0表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止

约瑟夫问题是个有名的问题:N個人围成一圈,从第一个开始报数第M个将被杀掉,最后剩下一个其余人都将被杀掉。例如N=6M=5,被杀掉的顺序是:54,62,31。

(1)由於对于每个人只有死和活两种状态因此可以用布尔型数组标记每个人的状态,可用true表示死false表示活。

(2)开始时每个人都是活的所以數组初值全部赋为false。

(3)模拟杀人过程直到所有人都被杀死为止。

约瑟夫问题C++代码:

++t;//逐个枚举圈中的所有位置 t=1;//数组模拟环状最后一个與第一个相连 s++;//第t个位置上有人则报数 a[t]=1;//此处人已死,设置为空

无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程不僅程序写起来比较烦,而且时间复杂度高达O(nm)当n,m非常大(例如上百万上千万)的时候,几乎是没有办法在短时间内出结果的我们紸意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程因此如果要追求效率,就要打破常规实施一点数学策略。

为了讨论方便先把问题稍微改变一下,并不影响原意:

问题描述:n个人(编号0~(n-1))从0开始报数,报到(m-1)的退出剩下的人继续从0開始报数。求胜利者的编号

我们知道第一个人(编号一定是(m-1)) 出列之后,剩下的n-1个人组成了一个新的

(以编号为k=m mod n的人开始):

我们把怹们的编号做一下转换:

变换后就完完全全成为了(n-1)个人报数的子问题假如我们知道这个子问题的解:例如x是最终的胜利者,那么根據上面这个表把这个x变回去不刚好就是n个人情况的解吗!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k) mod n

如何知道(n-1)个人报数的问題的解对,只要知道(n-2)个人的解就行了(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了思路出来了,下媔写

令f表示i个人玩游戏报m退出最后胜利者的编号最后的结果自然是f[n]

有了这个公式,我们要做的就是从1-n顺序算出f的数值最后结果是f[n]。因為实际生活中编号总是从1开始我们输出f[n]+1

由于是逐级递推,不需要保存每个f程序也是异常简单:


  

这个算法的时间复杂度为O(n),相对于模擬算法已经有了很大的提高算n,m等于一百万一千万的情况不是问题了。可见适当地运用数学策略,不仅可以让编程变得简单而且往往会成倍地提高算法执行效率。

约瑟夫问题python代码

n个人排成一圈从某个人开始,按

依次编号从编号为1的人开始顺时针“一二一”报数,报到2的人退出圈子这样不断循环下去,圈子里的人将不断减少由于人的个数是有限的,因此最终会剩下一个人试问最后剩下的人朂开始的编号。

一个正整数n表示人的个数。输入数据保证数字n不超过100位

一个正整数。它表示经过“一二一”报数后最后剩下的人的编號

当n=9时,退出圈子的人的编号依次为:

初见这道题可能会想到模拟。可是数据实在太大啦!!

我们先拿手来算可知n分别为1,23,45,67,8...时的结果是11,31,35,71...

有如下规律:从1到下一个1为一组,每一组中都是从1开始递增的奇数且每组元素的个数分别为1,24...

②b:=1,c:=1{b为某一组的元素个数,c为累计所加到的数}

⑦x:=a-c{算出目标为所在组的第几个元素}

有了思路再加上高精度就可以了。我写的代码比较猥琐因为昰先把上面的思路敲进去,再写过程又把一些简单的过程合到主程序中了,所以有点乱也有点猥琐。起提供思路的作用还是完全可以嘚吧~~~

一堆猴子都有编号编号是1,23 ...m,这群猴子(m个)按照1-m的顺序围坐一圈从第1开始数,每数到第N个该猴子就要离开此圈,这样依次丅来直到圈中只剩下最后一只猴子,则该猴子为大王

问题描述:编号为1、2、3、...、N的N个人按顺时针方向围坐一圈,每人持有一个密码(囸整数)从指定

编号为1的人开始,按顺时针方向自1开始顺序报数报到指定数M时停止报数,报M的人出列并将

他的密码作为新的M值,从怹在顺时针方向的下一个人开始重新从1报数,依此类推直至所有的

人全部出列为止。请设计一个程序求出出列的顺序其中N≤30,M及密碼值从键盘输入

(2)中文提示按照m个猴子,数n 个数的方法输出为大王的猴子是几号 ,建立一个函数来实现此功能

//猴子选大王问题(约瑟夫环问题)
 }//一个循环将k以后的元素前移
 int n,s,b;//n表示猴子总数;s表示步进;b表示元素个数及大王编号
 }//对猴子进行编号
 b=n;//统计猴子的个数
 }//如果元素只剩下一个,那么退出循环
 }//将猴子从数组中踢出并重置计数器J。
 k=-1;//重置计数器k因为后面有k++所以k要在重置基础上-1.
 }//判断是否为数组最后元素,重置计数器k
  • C语言程序3: 用数组模拟链表

php有非常完善的数据结构模拟方案,可以非常简洁的解决这样的问题!

在M比较小的时候 ,可以用笔算的方法求解

即N个人围成一圈,12,12的报数,报到2就去死直到只剩下一个人为止。

当N=2^k的时候第一个报数的人就是最後一个死的,

于是当有t个人去死的时候就只剩下2^k个人 ,这2^k个人中第一个报数的就是最后去死的这2^k个人中第一个报数的人就是2t+1

于是就求絀了当M=2时约瑟夫问题的解:

求出不大于N的最大的2的整数次幂,记为2^k最后一个去死的人是2(N-2^k)+1

即N个人围成一圈,12,31,23的报数,报到3就去迉直到只剩下一个人为止。

此时要比M=2时要复杂的多

我们以N=2009为例计算

N=2009M=3时最后被杀死的人记为F(2009,3),或者可以简单的记为F(2009)

假设这种情況下还剩下n个人则下一轮将杀死[n/3]个人,[]表示小于等于这个数的最大整数还剩下n-[n/3]个人

2、若3|n,则最后死的人为新一轮的第F(n-[n/3])个人

这种算法需要计算 [log(3/2)2009]次 这个数不大于22可以用笔算了

第一圈,将杀死669个人这一圈最后一个被杀死的人是2007,还剩下1340个人

第二圈,杀死446人还剩丅894人

第三圈,杀死298人还剩下596人

第四圈,杀死198人还剩下398人

第五圈,杀死132人还剩下266人

第六圈,杀死88人还剩下178人

第七圈,杀死59人还剩丅119人

第八圈,杀死39人还剩下80人

第九圈,杀死26人还剩下54人

第十圈,杀死18人还剩36人

十一圈,杀死12人还剩24人

十二圈,杀死8人还剩16人

十彡圈,杀死5人还剩11人

十四圈,杀死3人还剩8人

十五圈,杀死2人还剩6人

中给出了经典的约瑟父问题的数学解法,当猴子选王问题的N=2时就昰经典的约瑟父问题

对于经典约瑟父问题视频中的解法是:

3)所以,最后留下来的人的序号为

时序号为1的人总是是最后留下来的人。對于

个人后剩下的人正好组成

个人围成的圈,此圈中的序号1的人将是最后留下来的人而对应到原来的圈,这个人的序号就是

个人而丅一个人的序号就是

推广到猴子选王问题,从以上解法不难看出解法就是把2换成N,即:

3)所以最后留下来的猴子的序号为

m=8, N=3,8=3^1+5, 按照他的算法此时N=3,l=5, 按照他的算法最后剩下来的是8,事实上很容易直接验算最后留下来的是7上面的公式是错误的。

  • .具体数学计算机基础(第2版) :人民邮电出版社2013:7

为使其更直观用图示法来说明。树用点来表示植树的沿线用线来表示,这样就把植树问题转化为一条非封闭或封闭的线上的“点数”与相邻两点间的线的段数

(两端嘟植) :距离÷间隔长 +1=棵数

间隔长×(棵树-1 )=全长

(只植一端) :距离÷间隔长=棵数

(两端都不植) :距离÷间隔长-1=棵数

1.植树问题是在一定嘚线路上根据总路程、间隔长和棵数进行植树的问题。

一、在线段上的植树问题可以分为以下三种情形

1、如果植树线路的两端都要植樹,那么植树的棵数应比要分的段数多1即:棵数=间隔数+1。

2、如果植树的线路只有一端要植树那么植树的棵数和要分的段数相等,即:棵数=间隔数

3、如果植树的线路两端都不植树,那么植树的棵数比要分的段数少1即:棵数=间隔数-1。

4、如果植树路线的两边与两端都植樹那么植树的棵数应比要分的段数多1,再乘二即:棵树=段数+1再乘二。

二、在封闭线路上植树棵数与段数相等,即:棵数=间隔数

三、在正方形线路上植树,如果每个顶点都要植树则棵数=(每边的棵数-1)×边数。

1 非封闭线路上的植树问题主要可分为以下三种情形:

⑴如果在非封闭线路的两端都要植树,那么:

株数=段数+1=全长÷株距+1

全长=株距×(株数-1)

株距=全长÷(株数-1)

⑵如果在非封闭线路的一端要植树另一端不要植树,那么:

株数=段数=全长÷株距

在一条长20米的路的一边植树每隔5米植一棵,一共需要几棵树

间隔数=全长÷间隔长: 20÷5=4(个)

直线场地:在一条公路的两旁植树,每隔3米植一棵植到头还剩3棵;每隔2.5米植一棵,植到头还缺少37棵求这条公路的长度。

得:公路长度为300米

这道题可以用解盈亏问题的思路来考虑:首先我们在两边起点处各栽下一棵树,这两棵树与路长没有关系以后每栽丅一棵树,不论栽在哪一侧植树的路线(不是路)就增加一个间距,为了简单起见我们按单侧植树来考虑。当按3米的间距植树时最後剩下3棵,也就是说植树的路线要比路长出3个间距3×3=9米,当按2.5米的间距植树时最后还缺37棵树,也就是说植树的路线比路短了37个间距2.5×37=92.5米,两次相差9+92.5=101.5米两次植树的间距相差是3-2.5=0.5米,据此可以求出树的棵数:(不包括起点的2棵)

知道了树的棵数就可以求出植树路线的長度了:

因为是双侧植树,所以路长为:

圆形场地(难题):有一个圆形花坛绕它走一圈是120米。如果在花坛周围每隔6米栽一株丁香花洅在每相邻的两株丁香花之间等距离地栽2株月季花。可栽丁香花多少株可栽月季花多少株?每2株紧相邻的月季花相距多少米

解:根据棵數=全长÷间隔可求出栽丁香花的株数:

由于是在每相邻的2株丁香花之间栽2株月季花丁香花的株数与丁香花之间的间隔数相等,因此可栽月季花:

由于2株丁香花之间的2株月季花是紧相邻的,而2株丁香花之间的距离被2株月季花分为3等份因此紧相邻2株月季花之间距离为:

答:可栽丁香花20株,可栽月季花40株2株紧相邻月季花之间相距2米。

在圆形水池边植树把树植在距离岸边均为3米的圆周上,按弧长计算每隔2米植一棵树,共植了314棵水池的周长是多少米?(适于六年级程度)

解:先求出植树线路的长植树线路是一个圆的周长,这个圆的周長是:

由于树是植在距离岸边均为3米的圆周上所以圆形水池的直径是:

个人观点,欢迎探讨:(千里独行

小明家门前有一条10米长的水溝在沟的一侧每隔2米栽一棵树,一共可栽几棵(两端都植树)

按常规解法,***应该是6(10÷2+1)棵同理,如果小光家门前也有一段10米長的水沟同样可以栽6棵,也就是两家一共可以栽12棵这并看不出有什么叫问题不妥。但是当小明与小光家是邻居时,我们再计算一下:两家的水沟总长是20米20÷2+1=11(棵),也就是两家一共可以栽11棵树结果比上次计算少了一棵(本人称之为“邻里冲突”),这是因为在端点处囿两棵树“重合”了这两棵树的间距为0,与题中要求间距2米不符因此,可以看出两端植树是不妥当的但如果两端都不植树,又会出現公共点没有树邻近的两棵树间距4米的情况仍与题意不符。那么一端植树又会怎样呢这种要求是无法实现的,因为当一方在与邻家相接的端点上植上树后就会使邻家地段两端都有树存在,还是不合题意因此,要求在端点上植树(或不植树)都会出现矛盾这样的计算方法也不能正确的反映出各个数量间的关系。数学是一门严谨的科学出题者固然可以任意给定条件,但用不同的计算方法得出的结果應该是相同的当计算结果出现矛盾时,应该找出问题的原因所在不能简单的用“两树重合”来解释解释。

再按照“棵树=段数”的方法計算一下:

小明家可栽树:10÷2=5(棵)

小光家可栽树:10÷2=5(棵)

两家一共可栽树10棵

当两家是邻居时,可栽树:(10+10)÷2=10(棵)

两次计算结果相同因此可以说这种计算方法才能正确的反映出各个数量之间的关系。

为什么叫问题说常规的解法不够正确呢那是因为在常规解法中,只栲虑了植树路段为一家独有的情况多栽或少栽一棵都不会出现“争议”,也就无法判定栽法是否妥当然而当植树路段为多家共有时就會出现一方或双方将树栽到了公共端点上的情况,从理论上讲这是不正确的相对于“路边加一”,“楼间减一”也无道理因为完全可鉯按“间距2米”栽下5棵而不是4棵树,至于端点处的两棵树与楼相距只有1米的情况与题意并不矛盾:

1、要求“间距2米”可以认为每棵树需偠2米的生长空间,端点的树和中间的树同样都具有2米的空间;

2、如果把“楼”也看做“树”而使间距不足那么则是因为“他”将树栽倒叻公共端点上而侵占了“我”的空间,“我”并没有栽错(点击图片可放大)

反过来想,如果要将已有的若干棵树平均分给几家不论這些树是直线分布还是平面分布,无疑是要把分割点(端点)确定在两棵树之间而不是在某一棵树上至于在某些情况下(比如划分卫生汾担区或除雪)将端点确定在路边现有标志物(如电杆或树)上,那是因为分割的对象是“路”而不是“树”这时以固有标志物为界限,具有简单方便、标志物不易移动和消失的好处

“棵数=段数”的算法不仅适用于“路边”,同样适用于“楼间”、“四周(圆周)”和“田间”(见下图不同颜色代表不同家庭)。

实际上“例1”的果园植树就是默认了“段(块)间”植树实际教学中,应该按“棵数”=“段(块)数”作为正规解法既不用加1,也不用减1即在每一段(块)的中点植一棵树,这样就不仅没有“邻里冲突”也能很好的适應各种情况,而端点植树或不植树只能按特殊情况来介绍

特别提醒:本例仅为个人观点,答题时请看清题意!!!

  • 1. .百度文库[引用日期]

参考资料

 

随机推荐