约 瑟 夫 环 问 题 的 三 种 解 法
- 格式:pdf
- 大小:151.36 KB
- 文档页数:4
约瑟夫环问题python解法约瑟夫环问题:已知n个人(以编号1,2,3.n分别表示)围坐在一张圆桌周围。
从编号为k的人开始报数,数到k的那个人被杀掉;他的下一个人又从1开始报数,数到k的那个人又被杀掉;依此规律重复下去,直到圆桌周围的人只剩最后一个。
思路是:当k是1的时候,存活的是最后一个人,当k=2的时候,构造一个n个元素的循环链表,然后依次杀掉第k个人,留下的最后一个是可以存活的人。
代码如下:class Node():def __init__(self,value,next=None):self.value=valueself.next=nextdef createLink(n):return Falseif n==1:return Node(1)root=Node(1)tmp=rootfor i in range(2,n+1):tmp.next=Node(i)tmp=tmp.nexttmp.next=rootreturn rootdef showLink(root):tmp=rootwhile True:print(tmp.value)tmp=tmp.nextif tmp==None or tmp==root: def josephus(n,k):if k==1:print('survive:',n)root=createLink(n)tmp=rootwhile True:for i in range(k-2):tmp=tmp.nextprint('kill:',tmp.next.value) tmp.next=tmp.next.nexttmp=tmp.nextif tmp.next==tmp:print('survive:',tmp.value)if __name__=='__main__':print('-----------------')josephus(10,2)print('-----------------')josephus(10,1)print('-----------------')输出结果如下:-------------------------------------分界线-----------------------------------------感谢大家建议,第一种方法是直观暴力裸搞,确实不太简洁,下面写出我的第二种方法,求模来搞起,代码少了一些,如下:def josephus(n,k):if k==1:print('survive:',n)people=list(range(1,n+1))while True:if len(people)==1:p=(p+(k-1))%len(people)print('kill:',people[p])del people[p]print('survive:',people[0])if __name__=='__main__':josephus(10,2)josephus(10,1)运行结果和上面一样。
约瑟夫环递推推导过程问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。
求胜利者的编号。
下面利用数学推导,如果能得出一个通式,就可以利用递归、循环等手段解决。
下面给出推导的过程:(1)第一个被删除的数为(m-1)%n;(2)第二论的开始数字为k,那么这n-1个数构成的约瑟夫环为k,k+1,k+2,...k-3,k-2做一个简单映射。
(p(x)=(x-k)%n)k--->0k+1--->1k+2--->2------k-2--->n-2这是一个n-1个人的问题,如果能从n-1个人的问题的解退出n 个人问题的解,从而得到一个递推公式,那么问题就解决了。
假如我们已经知道了n-1个人时,最后胜利者的编号为x,利用映射关系逆推,就可以得出n个人时,胜利者的编号为(x+k)%n。
其中k=m%n。
代入(x+k)%n<=>(x+(m%n))%n<=>(x%n + (m%n)%n)%n<=> (x%n+m%n)%n <=> (x+m)%n(3)第二个被删除的数为(m-1)%n-1(4)假设第三轮的开始数字为o,那这n-2个数构成的约瑟夫环为o,o+1,o+2,...,o-3,o-2。
继续做映射p(x)=(y-o)%(n-2)o--->0o+1--->1o+2--->2------o-2--->n-3这是一个n-2个人的问题。
假设最后胜利者为y,那么n-1个人时,胜利者为(y+o)%(n-1),其中o等于m%(n-1)。
代入可得(y+m)%(n-1)要得到n-1个人问题的解,只需要得到n-2个人问题的解,倒退下去。
只有一个人时,胜利者就是编号0.小面给出递推式:f(1)=0;f(i)=(f[i-1]+m)%i;(i>1)有了递推公式,实现就非常简单了,给出循环的两种实现。
裴度与柳宗元《溪居》阅读答案对比赏析【阅读理解题目】:溪居裴度门径俯清溪,茅檐古木齐。
红尘飘不到,时有水禽啼。
溪居柳宗元久为簪组累①,幸此南夷谪。
闲依农圃邻,偶似山林客。
晓耕翻露草,夜榜响溪石②。
来往不逢人,长歌楚天碧。
[注]①簪组:古代官吏的冠饰。
②榜(péng)进船。
此句意为天黑船归,船触溪石而有声。
(1)“红尘飘不到”、“来往不逢人”两句诗均描写“溪居”生活环境,其相同之处是什么,请简要分析。
(2)两首诗表达的情感同中有异,其不同点是什么?【参考答案】:(1)(4分)两句都有人迹罕至的意思(1分),写出溪居环境的偏远、僻静(2分),表现出诗人远离尘俗的心境(1分)。
(2)(4分)裴诗表达的是亲近自然(1分)的喜悦、悠然自得之情(1分),柳诗表达的是被贬后远离官场(1分)的轻松、聊以自慰之情(1分)。
(或:裴诗表达的是退居山野远离官场亲近自然的自娱自乐之情,是由衷喜爱和闲适;柳诗表达的是谪居山野脱离官场纵情山水的自愚自乐之情,有怡情山水之幸,又心有不甘)【对比赏析】:裴度曾任宰相,并以平淮、蔡功高封晋国公,位高爵显,致招嫉妒.数起数罢.终徙东都留守.他激流勇退,转亲丘壑.《溪居》一首,当为晚年之作.这首诗用欣赏的口吻描绘自己乡间别墅的幽雅环境.诗写得清淡脱俗,格调超逸高古这首诗是柳宗元贬官永州居处冉溪之畔时的作品。
全诗写谪居佳境,苟得自由,独往独来,偷安自幸。
前四句叙述到这里的原因和自己的行径。
后四句叙述自己早晚的行动。
首尾四句隐含有牢骚之意。
秦观、李清照《减字木兰花》阅读答案对比赏析【阅读理解题目】:减字木兰花李清照卖花担上,买得一枝春欲放。
泪染轻匀,犹带彤霞晓露痕。
怕郎猜道,奴面不如花面好。
云鬓斜簪,徒要教郎比并看。
《减字木兰花·卖花担上》是宋代女词人李清照的作品。
此词截取了作者新婚生活的一个侧面,通过买花、赏花、戴花、比花,生动地表现了女主人公天真、爱美情和好胜的脾性,显示了她放纵恣肆的独特个性。
解答分析推理类题目有排除法、代入法和假设法这三种常用方法,除此之外,常用的方法还有找突破口法和图表法,这两种方法都是直接推理的方法,要重点掌握。
在此,中公教育专家就这两种方法进行讲解。
一、找突破口法找突破口法就是快速找到解题切入点的方法。
通常当题干存在某个比较特殊的条件或者存在某个对象(条件)被反复提及的时候,这个(些)条件往往就是解题的突破口。
找突破口法的解题步骤:第一步,了解背景条件。
通读题干了解题目涉及的元素及题目要求,注意这些元素分类和属性如时间、位置以及数字等限制条件。
第二步,找到隐含条件。
题目中的条件可分成显性条件和隐含条件两种。
显性条件即原文段落已明确给出的条件,而隐含条件是必须通过推理才能得出的条件。
需要对题干显性条件综合、递推和简化从而得到隐含条件。
第三步,推理得出答案。
在挖掘到隐含条件的基础上进行推导,当把所有隐含的条件推出后,答案就可得出。
例题1:甲乙丙丁四人的车分别为白色、银色、蓝色和红色。
在问到他们各自车的颜色时,甲说:“乙的车不是白色。
”乙说:“丙的车是红色的。
”丙说:“丁的车不是蓝色的。
”丁说:“甲、乙、丙三人中有一个人的车是红色的,而且只有这个人说的是实话。
”如果丁说的是实话,那么以下说确的是()。
A.甲的车是白色的,乙的车是银色的B.乙的车是蓝色的,丙的车是红色的C.丙的车是白色的,丁的车是蓝色的D.丁的车是银色的,甲的车是红色的中公解析:首先通过阅读题干容可知要求根据四人所说的话来判断车的颜色的对应关系。
由提问可知丁说的话是实话,即“说实话的人的车是红色的,且甲、乙、丙三人中只有一人说实话”。
再观察甲、乙、丙的话,发现只有乙的话中提到了红色,可以此作为突破口。
显然,乙不可能说实话,否则乙和丙的车都是红色的,不符合题意;则可知丙的车不是红色的,那么丙说的也不是实话,则丁的车是蓝色的。
所以说实话的是甲,甲的车是红色的。
由甲的话“乙的车不是白色”是实话,可知乙的车是银色,则丙的车是白色的。
解环方程的基本思路和步骤
解环方程的基本思路和步骤如下:
1. 确定环中的未知数:首先要确定环中的未知数个数,记作n。
2. 列出环方程:根据题目给出的条件,将问题转化为环方程。
环方程是指环中每个未知数与相邻未知数之间的关系。
3. 按照规律列方程:根据环方程的特点,可以逐个列出每个未知数与相邻未知数之间的方程。
常见的规律包括等差数列、等比数列等。
4. 化简方程:对于复杂的方程,可以通过合并同类项、约分、配方等方法,将方程化简为更简单的形式。
5. 解方程:对于简化后的方程,可以使用代入法、整除法、消元法等方法求解未知数的值。
6. 检验解:得到解后,将解代入原方程进行检验,确保得到的解在原方程中成立。
7. 给出解的范围:根据具体题目的要求,给出解的范围或形式。
总体来说,解环方程的关键是找到环中的未知数之间的关系,列出方程,并通过适当的方法求解未知数的值。
教师资格证螺旋式答题法,又称为“循环渐进式”答题法,是一种在螺旋式上升过程中不断展开的答题技巧。
这种方法要求在回答问题时,从简单到复杂,从表面到深入,层层递进,使答案更具有逻辑性和深度。
具体来说,螺旋式答题法在教资考试中可以这样运用:
1. 仔细阅读题干:首先需要认真阅读题目,理解题目所问的内容和要求。
2. 列出已知条件:根据题目所给的条件,将已知的、明确的信息罗列出来。
3. 推断未知条件:根据已知条件和自身知识储备,推断出题目中未明确提到的信息。
4. 组织答案:将已知条件和推断出的未知条件组合成完整的答案。
5. 检查答案:检查答案是否符合逻辑,是否满足题目要求,是否有遗漏或错误。
这种答题方法有助于考生在教资考试中更好地组织答案,使答案更有条理、更深入,从而提高答题的效率和准确性。
专题25 子路、曾皙、冉有、公西华侍坐1.孔子和《论语》孔子(前551—前479),名,字,世人尊称孔子。
中国春秋末期和,学派的创始人。
春秋鲁国陬邑(今山东曲阜东南)人。
孔子少时“贫且贱”,年15岁立志求学,通过私人传授,博习诗书礼乐。
约30岁,在曲阜城北设学舍,开始私人讲学。
颜渊、曾点、子路等是最早的弟子。
50岁任鲁国中都宰,继升司寇,不久去职,率弟子历游宋、卫、陈、蔡、齐、楚等国。
由于春秋末期,战争不断,民不聊生,“礼崩乐坏”,孔子怀礼乐治国的理想,欲复兴周礼,然终不见用。
在外14年,68岁重返鲁国,政治上仍不得志乃生隐退思想,专力从事讲学著述,晚年整理“六经”。
直到逝世,弟子达人,身通六艺者余人。
孔子在当时已有“圣人”之称,自西汉罢黜百家,独尊儒术,经过孔子整理传授的儒家经典五经,被定为必读的教科书,儒家学说由此成为中国封建文化的核心,对整个封建时代的政治生活和精神生活起着指导的作用,从积极方面和消极方面都给中国文化教育的发展以极其深刻的影响。
《论语》是一部的散文集,它是孔子的门人和再传弟子所辑录的言行录,全面地反映了孔子的哲学、政治、文化和教育思想。
宋儒将它与合称为“四书”。
《论语》中有不少精辟的言论成为人们习用的格言和成语,对后来的文学语言有很大的影响。
2.给下面的字注音俟莫甫哂相撰铿沂喟雩冠馑3.找出下列各句中的通假字(1)鼓瑟希,铿尔(2)莫春者,春服既成(3)唯求则非邦也与4.找出下列各句中的词类活用。
(1)端章甫(2)风乎舞雩(3)三子者出,曾晳后(4)赤也为之小,孰能为之大(5)由也为之,比及三年,可使有勇(6)异乎三子者之撰5.说出下列各句的句式特征。
(1)以吾一日长乎尔(2)摄乎大国之间(3)加之以师旅,因之以饥馑(4)异乎三子者之撰(5)浴乎沂,风乎舞雩,咏而归(6)为国以礼(7)毋吾以也(8)不吾知也(9)如或知尔,则何以哉(10)夫三子者之言何如6.解释下列各句中的加点的词语。
约瑟夫斯问题约瑟夫问题维基百科,⾃由的百科全书跳到导航跳到搜索约瑟夫问题(有时也称为约瑟夫斯置换),是⼀个出现在计算机科学和数学中的问题。
在计算机编程的算法中,类似问题⼜称为约瑟夫环。
⼈们站在⼀个等待被处决的圈⼦⾥。
计数从圆圈中的指定点开始,并沿指定⽅向围绕圆圈进⾏。
在跳过指定数量的⼈之后,处刑下⼀个⼈。
对剩下的⼈重复该过程,从下⼀个⼈开始,朝同⼀⽅向跳过相同数量的⼈,直到只剩下⼀个⼈,并被释放。
问题即,给定⼈数、起点、⽅向和要跳过的数字,选择初始圆圈中的位置以避免被处决。
历史这个问题是以弗拉维奥·约瑟夫命名的,他是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时的问题。
三体问题的求解方法在力学中,三体问题指的是三个物体相互作用的问题。
虽然在日常生活中,我们不时会遇到涉及三个物体相互作用的问题,但是实际上,三体问题却是非常棘手的,因为没有任何一种通用的解法。
三体问题中的三个物体通常是指行星或者卫星等天体,而这些天体之间的相互作用往往由万有引力来描述。
然而,由于万有引力的非线性特性以及天体之间的相互作用方式复杂,使得三体问题成为了一个被众多天文学家和物理学家研究的难题。
尽管三体问题没有一个通用的解法,但是在过去的几个世纪里,人们已经发明了各种各样的求解方法。
下面就让我们来了解一下其中最重要的几种方法。
1. 径向速度法径向速度法是最早被提出来的一种三体问题求解方法。
在该方法中,三个行星被视为在一个平面内运动,且所有的运动仅限于平面内的直线运动。
这意味着,在任何时候,行星之间都是满足轨道方程的。
由于二元行星问题的解法已经被广泛的研究,因此径向速度法的难度主要在于求解第三个行星的运动方程。
该方法早期的版本要求算术精度非常高,但是随着计算机技术的迅速发展,径向速度法已经成为了一种非常可靠的方法。
2. 多项式级数法多项式级数法是一种比较新的三体问题求解方法,最初由P. A. Lauber在20世纪90年代提出来的。
该方法的核心思想是将三体问题转化为多项式级数问题。
该方法要求首先将所有天体之间的相互作用分离成单独的X、Y和Z方向上的作用力,然后将时间和位置表示成多项式级数形式。
之后,通过运算得出行星在不同时间点的位置和速度。
多项式级数法可以极大的提高算法的速度和精度,但是它也有一些缺点,比如不能精确地模拟出包括运动细节在内的所有行星运动。
3. 散射角法散射角法是一种用来求解三体问题的数值方法。
该方法的核心思想是将三体问题转化为一个二元散射角问题。
在该方法中,三个行星之间的相互作用可以看作是由一组二元组合而成的。
该方法要求首先在三个行星间进行随机散射,并记录下每个散射过程中的初始抛物线状态。
约瑟夫环问题的两种解法(详解)约瑟夫环问题的两种解法(详解)题⽬: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。
2023届上海市青浦区高三二模语文试题(含答案)一、非连续性文本阅读阅读下文,完成下面小题。
材料一:①罗兰·巴特在《文之悦》里说:“文即织物。
”对中国人来说,这很容易理解。
《说文》释“文”的本义即“错画”。
所以在中文里,文章被视为一种经纬交错之物,也就与别的文章有千丝万缕的牵连。
根据互文性理论,任何文本都是由诸多前/潜文本中引出而重编的新的织品。
②巴特所见的“织文”,何尝不就是王安石所讥议的“编事”?但是李商隐的高妙之处在于,他总是做得到“用旧合机”“用人若己”。
他以一个统一的意思,让不同的文本像橘瓣一样聚焦,以己意与原典相发明,“化堆垛为云烟”。
这两相发明的效用,在于作者互文的拼贴物,将其原有的更为丰富的内容带进了新的场合。
③刘若愚曾就李商隐《锦瑟》一诗的众说纷纭写道:这首诗存在这么多论争,可见大多数读者都受到此诗的强烈影响,并且感觉到了诗中具体表现出来的某一境界,尽管在描述这个境界与形容对此一境界的反应时,他们会有很大差别。
一些批评家可能误解了此一诗的境界的本质,错认了他们反应的缘由,但这并不意味着他们的反应不真实。
④我们试着从互文性角度理解这一问题。
一个繁复的文本总是能够唤起读者的多重阅读体验,像刘若愚说的,他们的反应并非不真实,然而有一些反应没有正确缘由,这就是误读。
尽管从“诗无达诂”的意义上说,没有哪种阅读不是误读,一如哈罗德·布鲁姆所说,没有哪种解释不是曲解。
但读者寻找唯一正确的解释的冲动绝不会停止。
元好问《论诗三十首》之十二曰:“望帝春心托杜鹃,佳人锦瑟怨华年。
诗家总爱西昆①好,独恨无人作郑笺。
”中国传统的笺注,大抵只注出处,认为出处明意义自明,如果典故都已精确制导,规限了读者的反应,则可以一举中的。
如果发生理解上的分歧,一定是互文关系挖掘不力。
高阳在《〈锦瑟〉详解》一文中的看法,就代表了这种自信:“我作考证,以穷极源流为尚;义山诗号称难解,但本此义一字不放过,解亦不难。
约瑟夫环问题一、前言约瑟夫环(Josephus )问题是由古罗马的史学家约瑟夫(Flavius Josephus )提出的。
该问题的说法不一,传说他参加并记录了公元66—70年犹太人反抗罗马的起义。
约瑟夫作为一个将军,设法守住了裘达伯特城达47天之久,在城市沦陷之后,他和40名死硬的将士在附近的一个洞穴中避难。
在那里,这些叛乱者表决说“要投降毋宁死”。
于是,约瑟夫建议每隔两个人杀死一人,而这个顺序是由抽签决定的。
约瑟夫有预谋地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了另一个幸存者一起投降了罗马。
假设现在一个房间内共有n 个人。
同上所述,“杀人狂” 只想留下一个人活命,并且他将按下面的规则去杀人:● 所有的人围成一圈;● 顺时针报数,每次报到q 的人将被杀掉; ● 被杀掉的人将从房间内移走;● 然后从被杀掉的下一个人重新报数,直到剩余一人。
你非常不幸地参加了这场“游戏”,当然,你是想活命的,所以你必须快速决定要站到哪一个位置,才能使得最后留下的人是你。
这就是最初的“约瑟夫环”问题,纯粹的数学计算无法做出解答,但对于参加信息学竞赛的选手来说,可以快速的编写一个程序来解决。
下面就针对这个问题进行分析并解答。
二、特例当q=2时,是约瑟夫环的一个特例,可以利用数学的方法快速求解。
分析:如果只有2个人,显然剩余的为1号。
如果有4个人,在第一轮移除掉2、4以后,只剩下1、3,现在环中又仅剩下2个人了,我们已经知道2个人时,结果为1,所以当n=4时,最后剩余的也为1号。
如此可设想:当()02≥=k n k时,最后剩余的必定为1。
我们定义()n J 为由n 个人的构建成的约瑟夫环最后的结果,则有()12=kJ 。
让我们来证明该设想的正确性:11222--=-=k k k n (第一轮后,移除掉122/2-=k k 个人,剩余12-k ,1号仍在环中) 221222---=-=k k k n (第二轮后,1号仍在环中)……112222=-=n (1号仍在序列当中)()12=J在上面的分析中,每一轮的移除操作,均移除一半出列,1号总在剩余的环中。
约瑟夫环问题的简单解法(数学公式法)
关于约瑟夫环问题,无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达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-2 – n-2
k-1 – n-1
解x’ —- 解为x
注意x’就是最终的解
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,
相信大家都可以推出来:x’=(x+k)%n
如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。
(n-2)个人的解呢?当然是先求(n-3)的情况—- 这显然就是一个倒推问题!下面举例说明:
假设现在是6个人(编号从0到5)报数,报到(2-1)的退出,即 m=2。
那么第一次编号为1的人退出圈子,从他之后的人开始算起,序列变为2,3,4,5,0,即问题变成了这5个人报数的问题,将序号做一下转换:
现在假设x为0,1,2,3,4的解,x’设为那么原问题的解(这里注意,2,3,4,5,0的解就是0,1,2,3,4,5的解,因为1出去了,结果还是一个),根据观察发现,x与x’关系为x’=(x+m)%n,因此只要求出x,就可以求x’。
x怎么求出呢?继续推导吧。
0,1,2,3,4,,同样是第二个1出列,变为(2,3,4,0),转换下为
很简单,同样的道理,公式又出来了,x=(x”+m)%5,这里变成5了。
即求n-1个人的问题就是找出n-2的人的解,n-2就是要找出n-3,等等
因此,就可以回去看上面的推导过程了。
好了,思路出来了,下面写递推公式:
令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
递推公式
f[i]=(f[i-1]+m)%i; (i1)
有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。
因为实际生活中编号总是从1开始,我们输出f[n]+1 由于是逐级递推,不需要保存每个f[i],程序也是异常简单:
#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 ("The winner is %d", s+1);
这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。
算n,m等于一百万,一千万的情况不是问题了。
可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。
else {x=(jos(n-1,k)+k)%n;if(x==0)x=n;}
for (int i = 2; i = number; i++) {
System.out.println("出圈的顺序为:");
编号为4的人又从1开始报数,这时编号为4的人是这个队伍的头,则第二轮死去的人是6号。
50 public int PlayNum { get; set; }-*每次游戏丢PlayNum 次手绢*-
又假设关键数(要数的那个数)m=3,那么第一轮出局的将会是表头下一个的下一个(表头的第三个),如图所示:
std::cout"The link is must be longer than 1"endl;
约瑟夫环问题:已知n个人(以编号1,2,3.n分别表示)围坐在一张圆桌周围。
从编号为k的人开始报数,数到k的那个人被杀掉;他的下一个人又从1开始报数,数到k的那个人又被杀掉;依此规律重复下去,直到圆桌周围的人只剩最后一个。
--总共有13人,从第1位开始报数,每隔两位踢出1个。
?
{f(1)=0f(n)=(f(n?1)+m)%nbegin{cases}f(1) = 0f(n) = (f(n-1)+m)%nend{cases}{f(1)=0f(n)=(f(n?1)+m)%n?。