约瑟夫环
约瑟夫环的知识点总结
约瑟夫环的知识点总结约瑟夫环这个问题不仅在古代受到了广泛的关注,而且在现代数学中也有着重要的地位。
它涉及到了排列、递推、循环和递归等多个数学概念,并且有着一些有趣的数学特性。
因此,学习约瑟夫环不仅能够增加我们对于数学问题的理解,而且也可以提高我们的数学思维能力。
接下来,我们将从几个方面对约瑟夫环进行深入的讨论。
1. 约瑟夫环的历史约瑟夫环最早出现在约瑟夫斯的《犹太古记》中,他描述了犹太人在与罗马军队的战斗中围攻马萨达城的情景。
根据《犹太古记》的记载,当罗马军队攻陷了马萨达城后,大约960名男子决定宁死不从。
于是,他们站成一个圈,每隔两个人就有一个杀掉,直到最后只剩下一个人。
而这个幸存者恰恰就是约瑟夫斯本人。
因此,这个问题就得名为约瑟夫环。
除了这个故事之外,约瑟夫环在古代数学文献中也有着多次的提及。
例如,中国古代数学家秦九韶在其著作《数书九章》中也提到了这个问题。
他利用递推的方法解出了约瑟夫环的一般解,并推广到了更一般的情况。
自古代以来,约瑟夫环一直受到数学家们的关注,他们提出了很多不同的方法来解决这个问题。
而到了现代,约瑟夫环在计算机科学和密码学中也有着广泛的应用。
因此,约瑟夫环问题可以说是一个古老而又具有重要意义的数学问题。
2. 约瑟夫环的一般解在数学中,我们可以用递推的方法对约瑟夫环进行求解。
假设有N个人站成一圈,编号从0到N-1,而每隔M个人就有一个人出列。
那么一个简单直接的方法就是用递归来求解。
具体来说,我们可以定义一个递归函数f(n, m),表示N个人中最后存活下来的那个人的编号。
那么这个函数的递归关系可以如下定义:f(n, m) = (f(n-1, m) + m) % n其中f(1, m) = 0,表示只有一个人时的情况。
通过递归的方法,我们可以得到约瑟夫环的一般解。
而根据这个递归关系,我们还可以得到一些有趣的数学性质。
例如,我们可以求解约瑟夫环在给定N和M的情况下的解,而不需要实际模拟整个过程。
约瑟夫环公式
约瑟夫环公式约瑟夫环:编号从0开始,第⼀个出去的⼈是(k-1)%n,重新编号,出去的⼈的下⼀位编号为0,以此类推,最后⼀个出去的⼈的编号⼀定为0,f[1] = 0;当第⼀个⼈出去后,剩下n – 1 个⼈出去编号f[9] =(k - 1) % (n – 1), 还原原来队列编号(f[n - 1] + k) % (n – 1 + 1);1. 编号从0开始2. 每出去⼀个⼈重新编号3. 还原原排列公式:f[x] = (f[x] + k) % (x + 1) (不断+k模原⼈数+1直到原⼈数+1=n);得初始值n个⼈,数k个数第⼀个出去:剩n⼈;初始值:f[n] = (k - 1) % n;第⼆个出去:剩n - 1⼈;初始值:f[n - 1] = (k - 1) % (n - 1);第三个出去:剩n - 2⼈;初始值:f[n - 2] = (k - 1) % (n - 2);。
还原原排列编号有了初始值,接下来还原编号即可第⼀个出去:剩n⼈,⽆需还原第⼆个出去:剩n - 1⼈,f[n - 1] = (f[n - 1] + k) % n;第三个出去:剩n - 2⼈,f[n - 2] = (f[n - 2] + k) % (n - 1), f[n - 2] = (f[n - 2] + k) % n;。
就是不断 +k 模⼈数+1;直到⼈到n个代码#include <cstdio>#define N 100001int n,k;int f[N];int main(){scanf("%d%d", &n, &k);for(int i = n; i >= 1; --i){f[i] = (k - 1) % i;for(int j = i + 1; j <= n; ++j)f[i] = (f[i] + k) % j;printf("%d ", f[i] + 1);//输出编号+1,因为从0开始编号}return0;}。
约瑟夫环知识点总结
约瑟夫环知识点总结1. 约瑟夫环的数学模型约瑟夫环可以用数学的方式进行建模和解决。
通常情况下,我们把约瑟夫环的问题理解为一个数学公式的求解。
假设n个士兵分别编号为1、2、3、...、n,m为出列的间隔数。
首先,我们可以得到第一个出列的士兵编号为(m-1)%n+1,例如当n=7,m=3时,第一个出列的士兵为(3-1)%7+1=3。
之后,每次出列后的编号变换规律为:下一个出列士兵的编号为前一个出列士兵编号加上m在n取模后的结果,并且再对n取模,即f(i)=f(i-1)+m)%n。
以上公式是解决约瑟夫环问题的核心,因为根据这个公式可以有效地计算出每一轮出列的士兵的编号。
然后我们只需要循环迭代这个公式,直到最后只有一个士兵为止,这个士兵的编号就是最后的结果。
2. 约瑟夫环的递归解法除了上述的数学模型,还可以使用递归的方法来解决约瑟夫环的问题。
递归是一种非常高效的解决问题的方法,适用于很多数学问题,包括约瑟夫环的计算。
递归方法的求解思路是:先假设已知了n-1个士兵的约瑟夫环问题的解f(n-1, m),那么我们要求的n个士兵的约瑟夫环的解f(n, m)可以通过以下方式推导得到。
首先,第一个出列的士兵编号为(m-1)%n+1,之后剩下的n-1个士兵重新排列成一个圆圈,编号重新从1到n-1。
将这n-1个士兵的解f(n-1, m)映射到n个士兵的解f(n, m)上,此时,再回到上述的数学模型进行计算,找到最终的结果。
递归的思路虽然清晰,但是在实际求解的过程中,由于递归的不断嵌套,计算量会非常庞大,不适合解决大规模的约瑟夫环问题。
3. 约瑟夫环的迭代解法在解决实际问题的时候,我们更多地使用迭代的方法来求解约瑟夫环的问题。
迭代的思路是从最简单的情况开始,然后不断迭代得到更加复杂的情况的解。
对于约瑟夫环问题,迭代的思路是逐步得出每一轮出列的士兵的编号并记录下来,直到剩下最后一个士兵为止。
通常情况下,我们会使用一个数组或者链表来保存每一轮出列的士兵的编号,最后得出最后一个士兵的编号。
约瑟夫环
开始
输入m和n
创建链表
k>n-1 N 移动指针p
Y
删除p后一结点q
指针p后移,k++
输出n
结束
#include<iostream> 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);//计算环上所有人数函数
• 使用程式来求解的话,只要将阵列当作环 状来处理就可以了,在阵列中由计数1开始, 每找到三个无资料区就填入一个计数,直 而计数达41为止,然后将阵列由索引1开始 列出,就可以得知每个位置的自杀顺序, 这就是约瑟夫排列,41个人而报数3的约瑟 夫排列如下所示: • 14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23 • 由上可知,最后一个自杀的是在第31个位 置,而倒数第二个自杀的要排在第16个位 置,之前的人都死光了,所以他们也就不 知道约瑟夫与他的朋友并没有遵守游戏规 则了。
• 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); • }
细究“约瑟夫环”
细究“约瑟夫环”0 引言17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。
问怎样排法,才能使每次投入大海的都是非教徒。
1 问题描述有n个人围成一圈,顺序排号。
从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
示例一:输入:n = 3输出:“2”解释:首先轮流报数,3就被退出了,之后1,2,1,1就被退出了,最后只剩下了2。
2 算法描述解题思路:首先因为考虑到是不断的有规律退出数字则首先要考虑到循环的使用,我们从索引上看,如果将每次循环的三人看成一组,则被退出的人的索引为2,此时我们就知道了我们删去的就应该是索引为2的人。
但我们此时又想到该如何让其满足我们想得到的方式呢?我们不妨将其横排排列,123删去3后变成1212,此时我们发现可以将之前还未删去的数值排列在其之后,我们可以多举几个例子如1234变成12412。
那么此时我们就解决了第一个如何将其围成圈的问题,而之后就到了最关键的时候了,如何删去这些值?我们又举123为例,若想得到1,我们可以有很多的做法,而取余则是一种很巧的运算方式,如:“1”的位置是1,所以0%3(3是这三个值的长度(1,2,3))得到0,而1 的索引就是0,同理我们可得其他的值。
根据规律可得,若k=2的值为删去的数,那么我们只需进行k = k+2得到下一个删去的值。
(简单讲就是本事索引除以长度得到自身位置,本身长度加1除以长度得到下一个位置,同理加2)3 实验结果与讨论通过编程最终求出了约瑟夫环的问题。
附件代码清单用python求出杨辉三角数4 结语约瑟夫环是一个很经典的数学问题,其中的解法多种多样,通过这种复杂的循环可以使我们很轻松的解决一些问题。
约瑟夫之谜的名词解释
约瑟夫之谜的名词解释约瑟夫之谜:逃避死亡的智慧在古老的历史长河中,有许许多多的谜团流传至今,其中一道引人瞩目的谜题,就是约瑟夫之谜。
这个谜题如同一道脑筋急转弯,令人陷入思考的深渊中。
究竟什么是约瑟夫之谜?本文将以一种旁征博引的方式,深入探讨这个神奇而又深沉的谜题。
约瑟夫之谜,也被称为约瑟夫环问题,源自古罗马时期。
据传,约瑟夫是一位年纪轻轻却头脑聪慧的犹太人。
敌对的罗马军队包围了他的城市,他们的终极目标就是夺取约瑟夫的性命。
为了逃过一劫,约瑟夫和他的朋友们决定采取一种不同寻常的生存策略。
命运的时间步骤过于残酷:每隔一定的时间,罗马军队会选择一个固定的顺序排列的人杀掉。
为了保住自己的生命,约瑟夫们决定在一个迷人而又难解的游戏中挣扎。
他们决定按照固定的顺序围着一个圆圈坐下,每当轮到某个人遭到宣判时,他后面的人会立即被杀掉。
当约瑟夫和他的朋友们面对这个可怕而又无情的游戏时,他们拥有一个独特的优势:他们都是聪明的思考者。
他们知道,唯一可以保证自己生命延续的方法就是找到一个特殊的座位,以便在他们被罗马军队宣判之前逃走。
而解决此难题的方法,正是约瑟夫之谜的核心。
约瑟夫和他的朋友们经过长时间的思考和讨论,最终得出结论:他们必须假装接受宣判并立即逃走。
他们以此方式不断循环,直到只剩下最后一个人。
通过观察若干个这样的循环,他们发现一个神奇的数学准则:最后一个幸存者的座位总是环中的第n个人。
找到这个规律之后,约瑟夫和他的朋友们在面对死亡的恐惧时,找到了一丝希望和智慧。
他们可以以这种方式延长自己的存活时间,尽管最终没有人能够永远逃脱。
但这个故事中的约瑟夫告诉我们,面对绝望时,思考和创新是人类的强大武器。
约瑟夫之谜,通过一种看似无意义的数学模式,揭示了我们在面对逆境时的思考方式。
这个谜题也引起了许多数学家和研究者的兴趣,他们试图寻找背后的数学规律和解释。
在当代,约瑟夫之谜被广泛应用于许多领域,如计算机科学、游戏理论和密码学等。
约 瑟 夫 环 问 题 的 三 种 解 法
Java 约瑟夫环问题的两种解法(循环数组,单向环形链表)Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1=k=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
2.解决方法一:循环数组提示:每次报数,如果满足出圈的条件就将数组元素设置为-1,当下次报数时跳过-1。
直至数组最后一个元素变为-1,循环结束,数组的循环使用取模来完成。
--解决方法二:数组+取模public static void JosephuByArr(int total,int startNum,int m){ int []Arr=new int[total];int leave=total; --剩下的数量int count=0; --报数(0-1-2-3-4-5····)int index=startNum; --第一个元素--初始化数组(为了方便取模,最后一个元素放在数组的第0个,也可以先加一再取模)Arr[0]=total;for(int i=1;iArr.length;i++){Arr[i]=i;while(leave0){count++; --报数--找到报数为count的数组元素if(Arr[index%total]==-1){while(Arr[index%total]==-1){index++;--如果满足条件,输出(元素设置为-1)if(count%m==0){System.out.print(Arr[index%total]+"t");Arr[index%total]=-1;leave--;--下一个元素开始index++;3.解决方法二:单向环形链表提示:用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从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-2k-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’。
约瑟夫斯问题
约瑟夫斯问题约瑟夫问题维基百科,⾃由的百科全书跳到导航跳到搜索约瑟夫问题(有时也称为约瑟夫斯置换),是⼀个出现在计算机科学和数学中的问题。
在计算机编程的算法中,类似问题⼜称为约瑟夫环。
⼈们站在⼀个等待被处决的圈⼦⾥。
计数从圆圈中的指定点开始,并沿指定⽅向围绕圆圈进⾏。
在跳过指定数量的⼈之后,处刑下⼀个⼈。
对剩下的⼈重复该过程,从下⼀个⼈开始,朝同⼀⽅向跳过相同数量的⼈,直到只剩下⼀个⼈,并被释放。
问题即,给定⼈数、起点、⽅向和要跳过的数字,选择初始圆圈中的位置以避免被处决。
历史这个问题是以弗拉维奥·约瑟夫命名的,他是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时的问题。
约瑟夫环——递推公式
0123456789 12345678910 4567891012 789101245 10124578 4578101
810145
45810
1045
104
4约瑟夫环——递推公式
递推公式:
f(N,M)=(f(N−1,M)+M)%N
f(N,M)表⽰,N个⼈报数,每报到M时杀掉那个⼈,最终
f(N−1,M)表⽰,N-1个⼈报数,每报到M时杀掉那个⼈,最终胜利者的编号
现在假设有10个⼈,报到3的⼈就会被杀掉,我们⽤数字给这⼗个⼈编号为
1 2 3 4 5 6 7 8 9 10
第·⼀⾏绿⾊那⾏是数组下标,第⼆⾏是每个⼈的编号
现在逆向推导
f(1,3):只剩最后⼀个⼈,胜利者的数组下标为0
f(2,3)=(f(1,3)+3)%2=1,只有两个⼈的时候,胜利者下标为1。
f(10,3)=3,因为我们数组下标是从0开始的,所以⼈的编号是下标+1,也就是4
那么这个公式是怎么推导的呢?
1.假设我们已经知道了10个⼈时,胜利者的下标为3,那下⼀次9个⼈时,胜利者的下标为多少?
其实就是10个⼈时杀掉了编号为3(即数组下标为2)的⼈后,后⾯的⼈都往前移动了3位,所以胜利者的下标由3变成了0
2.那我们倒过来我们知道9个⼈时,胜利者的下标为0,那10个⼈时胜利者的下标为多少?
其实这和上⾯的问题⼀样,这是这是上个问题的逆过程,就是把⼤家都往后移动3位,所以f(10,3)=f(9,3)+3,不过可能会出现数组越界所以要取模变成f(10,3)=(f(9,3)+3)%10
3.那么⼈数改为n报到m时就杀掉数组怎么移动呢
⼀样的,杀⼀⼈则后⾯的⼈的下标都往前移动m则,f(n,m)=(f(n-1,m)+m)%n。
约瑟夫环问题的三种解法
约瑟夫环问题的三种解法约瑟夫问题是个著名的问题:N个⼈围成⼀圈,第⼀个⼈从1开始报数,报到k的⼈将被杀掉,接着下⼀个⼈⼜从1开始报,直到最后剩下⼀个,求最后留下的⼈的下标。
题⽬集合解法1:暴⼒可以直接暴⼒求解,时间复杂度为O(nk)解法2:递推设f(n,k)为当n个⼈围成⼀圈时,最后留下的⼈的下标。
对于f(n-1,k)来说,其结果相当于f(n,k)的结果向前移动k\%(n-1)位。
因为对于f(n,k)来说,去掉第⼀轮报的数(k\%n)后,现在就只剩下n-1个数,并且是以(k\%(n-1)+1)作为第⼀个数,即所有数向前移动k\%(n-1)位。
现在的结果就为f(n-1,k)对于f(5,3)来说,其结果为4。
当其去掉第⼀轮报的数后,其向前移动了(3\%4)位,以4为起始,f(4,3)结果为1,对应着f(5,3)的结果4向前移动了3位所以反过来看即为,即为f(n-1,k)的结果向后移动k\%(n-1)位即f(n+1,k)=(f(n,k)+k\%n)\%n (x下标从0开始,因为取模结果为[0,n-1])时间复杂度为O(n)ll josephus2(ll n,ll k){ll pos=0;for(int len=1;len<=n;len++){pos = (pos+k)%len;}return pos+1;}递推代码解法3:如果当前这⼀位⼈没被杀掉,则他可以放在幸存者的末尾,直到幸存者数量为1所以对于下标为i的⼈,如果在他前⾯已经被杀掉了q个⼈,那么他的新的下标为n+q(k-1)+x,(1\leq x <k)如下图所⽰,最后被淘汰的编号⼀定是n*k,所以幸存者最后的编号是n*k我们现在需要从幸存者最后的编号中恢复出最初编号假设幸存者这⼀次的编号为p os_{i},在他后⾯包括他还有x位幸存者,则[pos_{i-1},pos_{i})间⼀定有x个不能被k整除的数这样才能使在他后⾯包括他还有x位幸存者。
约瑟夫环问题知识点总结
约瑟夫环问题知识点总结约瑟夫环问题的描述如下:有n个人(编号从1到n),他们围成一个环形。
从第一个人开始报数,数到m的人被杀掉,然后从被杀掉的人的下一个人开始重新报数,直到所有人都被杀掉为止。
问题的解是最后剩下的那个人的编号。
约瑟夫环问题在计算机科学和数学领域都有着广泛的应用,因为它涉及到循环队列、递归、数学归纳法等多个概念。
以下是约瑟夫环问题的一些重要知识点总结:1. 约瑟夫环问题的递归解法递归是解决约瑟夫环问题的一种常见的方法。
基本思路是将问题分解为规模更小的子问题,并通过解决子问题来解决原始问题。
对于约瑟夫环问题来说,递归的解法是通过递归地计算每轮的幸存者,直到只剩下一个人为止。
递归解法的关键是找到问题的递归关系。
具体而言,对于约瑟夫环问题,如果用f(n, m)表示n个人中最后幸存者的编号,那么可以得出如下的递归关系:f(n, m) = (f(n-1, m) + m) % n其中,%表示取模运算。
该递归关系表明,当有n个人的时候,最后幸存者的编号可以通过n-1个人的最后幸存者的编号计算得到。
2. 约瑟夫环问题的迭代解法除了递归解法之外,约瑟夫环问题还可以通过迭代的方式进行求解。
迭代解法的基本思路是模拟报数和杀人的过程,直到最后只剩下一个人为止。
迭代解法的关键是找到每一轮报数的规律。
具体而言,对于约瑟夫环问题,可以用一个循环队列来模拟报数的过程,每次报数到第m个人就将其从队列中移除。
通过不断循环这个过程,最终可以得到最后幸存者的编号。
3. 约瑟夫环问题的数学解法约瑟夫环问题还可以通过数学的方法进行求解。
具体而言,可以利用数学归纳法来推导出约瑟夫环问题的解析表达式。
这种方法的优点是可以更快地得到结果,但是需要一定的数学推导能力。
通过数学推导,可以得到约瑟夫环问题的解析表达式:f(n, m) = (f(n-1, m) + m) % n其中,f(1, m) = 0。
该表达式可以直接求解出最后幸存者的编号。
约瑟夫环问题(最简单的数学解法)
约瑟夫环问题(最简单的数学解法)基本问题描述:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。
从编号为1的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列。
(也类似于变态杀⼈狂问题)通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。
通常,我们会要求输出最后⼀位出列的⼈的序号。
那么这⾥主要研究的是最后⼀个出列的⼈的序号要怎么确定。
当n,m数据量很⼩的时候,我们可以⽤循环链表模拟约瑟夫环的过程。
当模拟到⼈数等于1的时候,输出剩下的⼈的序号即可。
这种⽅法往往实现起来⽐较简单,⽽且也很容易理解。
但是时间复杂度却是很糟糕的,达到了O(n m),这样的话,其实在n,m⽐较⼤的时候(n m达到10^8或者更⼤),那么要得出结果往往需要耗费很长的时间,但是我们可以运⽤⼀点数学上的技巧,将最后结果推导出来。
为了简化出列的过程:⾸先我们把这n个⼈的序号编号从0~n-1(理由很简单,由于m是可能⼤于n的,⽽当m⼤于等于n时,那么第⼀个出列的⼈编号是m%n,⽽m%n是可能等于0的,这样编号的话能够简化后续出列的过程),当数到m-1的那个⼈出列,因此我们编号完成之后,开始分析出列的过程:第⼀次出列:⼀开始的时候,所有⼈的编号排成序列的模式即为:0,1,2,3,4,5...n-2,n-1那么第⼀次出列的⼈的编号则是(m-1)%n1,那么在第⼀个⼈出列之后,从他的下⼀个⼈⼜开始从0开始报数,为了⽅便我们设k1 =m%n1(n1为当前序列的总⼈数)那么在第⼀个⼈出列之后,k1则是下⼀次新的编号序列的⾸位元素,那么我们得到的新的编号序列为:k1,k1+1,k1+2,k1+3...n-2,n-1,0,1,2...k1-3,k1-2 (k1-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从0开始报数,那么在这个新的序列中,每个⼈报的相应数字为:0,1,2,3....n-2那么第⼆次每个⼈报的相应数字与第⼀次时⾃⼰相应的编号对应起来的关系则为:0 --> k11 --> k1+12 --> k1+2...n-2 ---> (k1+n-2)%n1(n1为当前序列的总⼈数,因为是循环的序列,k1+n-1可能⼤于总⼈数)那么这时我们要解决的问题就是n-1个⼈的报数问题(即n-1阶约瑟夫环的问题)可能以上过程你还是觉得不太清晰,那么我们重复以上过程,继续推导剩余的n-1个⼈的约瑟夫环的问题:那么在这剩下的n-1个⼈中,我们也可以为了⽅便,将这n-1个⼈编号为:0,1,2,3,4...n-2那么此时出列的⼈的编号则是(m-1) % n2(n2为当前序列的总⼈数),同样的我们设k2 = m % n2,那么在这个⼈出列了以后,序列重排,重排后新的编号序列为:k2,k2+1,k2+2,k2+3...n-2,n-1,0,1,2...k2-3,k2-2 (k2-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从1开始报数,那么在这个新的序列中,每个⼈报的相应数字为:1,2,3,4....n-2那么这样的话是不是⼜把问题转化成了n-2阶约瑟夫环的问题呢?后⾯的过程与前两次的过程⼀模⼀样,那么递归处理下去,直到最后只剩下⼀个⼈的时候,便可以直接得出结果当我们得到⼀个⼈的时候(即⼀阶约瑟夫环问题)的结果,那么我们是否能通过⼀阶约瑟夫环问题的结果,推导出⼆阶约瑟夫环的结果呢?借助上⾯的分析过程,我们知道,当在解决n阶约瑟夫环问题时,序号为k1的⼈出列后,剩下的n-1个⼈⼜重新组成了⼀个n-1阶的约瑟夫环,那么假如得到了这个n-1阶约瑟夫环问题的结果为ans(即最后⼀个出列的⼈编号为ans),那么我们通过上述分析过程,可以知道,n阶约瑟夫环的结果(ans + k)%n(n为当前序列的总⼈数),⽽k = m%n则有:n阶约瑟夫环的结果(ans + m % n)%n,那么我们还可以将该式进⾏⼀下简单的化简:当m<n时,易得上式可化简为:(ans + m)% n⽽当m>=n时,那么上式则化简为:(ans % n + m%n%n)% n即为:(ans % n + m%n)% n⽽(ans + m)% n = (ans % n + m%n)% n因此得证(ans + m % n)%n = (ans + m)% n这样的话,我们就得到了递推公式,由于编号是从0开始的,那么我们可以令f[1] = 0; //当⼀个⼈的时候,出队⼈员编号为0f[n] = (f[n-1] + m)%n //m表⽰每次数到该数的⼈出列,n表⽰当前序列的总⼈数⽽我们只需要得到第n次出列的结果即可,那么不需要另外声明数组保存数据,只需要直接⼀个for循环求得n阶约瑟夫环问题的结果即可由于往往现实⽣活中编号是从1-n,那么我们把最后的结果加1即可。
约 瑟 夫 环 问 题 的 三 种 解 法
约瑟夫问题(数学解法及数组模拟)约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。
在计算机编程的算法中,类似问题又称为约瑟夫环。
又称“丢手绢问题”.)据说著名犹太历史学家 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就能得到原来的编号。
简单的约瑟夫环算法
简单的约瑟夫环算法约瑟夫环问题起源于⼀个犹太故事。
约瑟夫环问题的⼤意如下: 罗马⼈攻占了桥塔帕特,41个⼈藏在⼀个⼭洞中躲过了这场浩劫。
这41个⼈中,包括历史学家Josephus(约瑟夫)和他的⼀个朋友。
剩余的39个⼈为了表⽰不向罗马⼈屈服,决定集体⾃杀。
⼤家制定了⼀个⾃杀⽅案,所有这41个⼈围成⼀个圆圈,由第⼀个⼈开始顺时针报数,每报数为3的⼈就⽴刻⾃杀,然后再由下⼀个⼈重新开始报数,仍然是每报数为3的⼈就⽴刻⾃杀......,直到所有的⼈都⾃杀⾝亡为⽌。
约瑟夫和他的朋友并不想⾃杀,于是约瑟夫想到了⼀个计策,他们两个同样参与到⾃杀⽅案中,但是最后却躲过了⾃杀。
请问,他们是怎么做到的?package .datastruct;import java.util.Scanner;//简单的约瑟夫环求解public class Josephus {static final int Num=41; //总⼈数static final int KillMan=3; //⾃杀者报数//约瑟夫环算法static void josephus(int alive){int []man = new int[Num];int count=1;int i=0,pos=-1;while(count<=Num){do{pos=(pos+1)%Num; //环处理if(man[pos]==0) //只有没⾃杀的⼈才不等于0i++;if(i==KillMan){ //该⼈⾃杀i=0;break;}}while(true);man[pos]=count;System.out.printf("第%2d个⼈⾃杀!约瑟夫环编号为%2d",pos+1,man[pos]);if(count%2==1){System.out.printf("->");}else{System.out.printf("->\n"); //输出换⾏}count++;}System.out.println();System.out.printf("这%d个需要存活的⼈的初始位置应该排在以下序号:\n",alive);alive = Num - alive;for(i=0;i<Num;i++){if(man[i]>alive)System.out.printf("初始编号:%d,约瑟夫环编号:%d\n", i+1,man[i]);}System.out.println();}public static void main(String[] args){int alive;System.out.print("约瑟夫环问题求解!\n");System.out.print("请输⼊需要留存的⼈的数量:");Scanner input = new Scanner(System.in);alive = input.nextInt();josephus(alive);}}。
约瑟夫环设计的知识点
约瑟夫环设计的知识点约瑟夫环(Josephus problem)是一个经典的数学问题,它的背后涉及到一些有趣的数学原理和设计思想。
本文将介绍约瑟夫环的背景、问题描述和解决方法,并介绍一些与约瑟夫环相关的知识点。
背景介绍:约瑟夫环问题最早出现在古代历史中,据说是由犹太历史学家弗拉维奥·约瑟夫斯(Flavius Josephus)提出的。
故事的背景是在公元1世纪犹太战争时,罗马军队包围了一个围城的犹太人民。
为了避免被俘或杀害,一群犹太人决定形成一个“死亡圈”,按照一定规则轮流自杀,直到最后一个人留下。
他们选择了一个合适的位置,每次报数到某个固定的数字时将当前的人杀掉,直到只剩下最后的一个人。
问题描述:现在我们将这个问题抽象为数学问题。
假设有n个人,从1到n编号,围成一个圆圈。
从编号为1的人开始报数,每次报到m的人将被淘汰出局,然后从下一个人重新开始报数。
重复这个过程,直到只剩下一个人。
问题的关键是确定最后剩下的人的编号。
解决方法:对于约瑟夫环问题,有很多种解决方法。
下面介绍其中两种常用的方法:1. 数学公式法:我们可以通过数学公式来解决约瑟夫环问题。
根据数学推导,对于给定的n和m,最后剩下的人的编号可以通过以下公式计算得出:f(n,m) = (f(n-1,m) + m) % n其中f(n,m)表示n个人中最后剩下的人的编号。
2. 循环链表法:另一种常用的解决约瑟夫环问题的方法是使用循环链表。
我们可以用一个循环链表模拟这个过程,每次找到要删除的节点并删除,直到只剩下一个节点为止。
这种方法的好处是时间复杂度较低,可以快速找到要删除的节点。
与约瑟夫环相关的知识点:除了约瑟夫环问题本身,还有一些与之相关的知识点,让我们更深入地了解这个问题。
以下是其中几个重要的知识点:1. 约瑟夫环的应用:约瑟夫环在现实生活中有一些应用。
例如,可以用来解决任务分配的问题。
假设有n个任务需要分配给m个人完成,可以通过构建一个约瑟夫环来确定每个任务分配给哪个人。
约瑟夫环问题的两种解法(循环链表和公式法)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述: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),显然,这不是⼀个好的算法。
约瑟夫环问题(Josephus)
第一行为一个整数T(<2^15)表示测 试次数,接着第二到T+1行分别为n,m和k 的值。 例:2
10 2 3
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。
(3)us 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]+" ");
68 34 25 38 54 4 120 16 23 32 53 97
500 12 30 166 358 266
实验总结:
(1) 经过这次的实践,让我们明白了合作 的重要性。
(2)在程序设计初期,总会或多或少的出现 问题,经过我们的耐心调试,不断地修改, 慢慢地将程序设计较好的符合了要求。
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。
精品约瑟夫环课程设计
精品 约瑟夫环课程设计一、课程目标知识目标:1. 学生能理解约瑟夫环的概念,掌握其数学模型及相关术语。
2. 学生能够运用所学知识解决约瑟夫环问题,如计算生存者位置、推导出递推公式等。
3. 学生了解约瑟夫环问题在计算机科学、数学等领域的应用。
技能目标:1. 学生通过分析约瑟夫环问题,培养逻辑思维和问题解决能力。
2. 学生能够运用编程语言实现约瑟夫环问题的求解,提高编程能力。
3. 学生通过小组讨论,学会合作学习,提高团队协作能力。
情感态度价值观目标:1. 学生在解决约瑟夫环问题的过程中,体验数学和计算机科学的趣味性,培养学科兴趣。
2. 学生通过面对挑战,克服困难,培养坚持不懈、勇于探索的精神。
3. 学生了解约瑟夫环问题背后的历史故事,激发对科学家的敬仰之情,树立正确的价值观。
课程性质:本课程为信息技术与数学跨学科课程,结合实际问题,培养学生的逻辑思维、编程能力和团队协作精神。
学生特点:学生处于高年级阶段,具备一定的数学基础和编程能力,对新鲜事物充满好奇心,喜欢挑战性任务。
教学要求:课程注重理论与实践相结合,以学生为主体,鼓励学生主动探究、合作学习,充分调动学生的积极性。
通过本课程的学习,使学生能够将所学知识应用于实际问题的解决中。
二、教学内容1. 约瑟夫环概念介绍:通过故事引入约瑟夫环问题,讲解其背景和基本原理,使学生理解问题的实质。
教材章节:第五章“递推与递归”第一节“约瑟夫环问题”2. 约瑟夫环数学模型:引导学生建立数学模型,推导递推公式,探讨求解方法。
教材章节:第五章“递推与递归”第一节“约瑟夫环问题”3. 编程求解约瑟夫环问题:介绍编程语言中的循环、条件语句等基本语法,指导学生编写程序求解约瑟夫环问题。
教材章节:第六章“程序设计基础”第二节“循环结构与应用”4. 约瑟夫环问题拓展与应用:分析约瑟夫环问题在计算机科学、数学等领域的应用,提高学生的问题解决能力。
教材章节:第五章“递推与递归”第三节“递归算法与应用”5. 小组合作与实践:组织学生进行小组讨论,共同解决约瑟夫环问题,分享编程经验和心得。
约瑟夫环问题小结
约瑟夫环问题⼩结⼀问题描述约瑟夫环问题的基本描述如下:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。
从编号为1的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,要求找到最后⼀个出列的⼈或者模拟这个过程。
⼆问题解法在解决这个问题之前,⾸先我们对⼈物进⾏虚拟编号,即相当于从0开始把⼈物重新进⾏编号,即⽤0,1,2,3,...n-1来表⽰⼈物的编号,最后返回的编号结果加上1,就是原问题的解(为什么这么做呢,下⽂有解释)。
⽽关于该问题的解通常有两种⽅法:1.利⽤循环链表或者数组来模拟整个过程。
具体来讲,整个过程很明显就可以看成是⼀个循环链表删除节点的问题。
当然,我们也可以⽤数组来代替循环链表来模拟整个计数以及出列的过程。
此处只给出利⽤数组来模拟这个过程的解法,最终结果为最后⼀个出列的⼈的编号:#include<iostream>#include<unordered_map>#include<queue>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<sstream>#include<set>#include<map>using namespace std;int main(){int n,m;cin>>n>>m;vector<int>rs(n);for(int i = 0 ; i < n; i++)rs[i] = i + 1;//对⼈物重新进⾏编号,从0开始int cur_index = 0;//当前圆桌状态下的出列⼈的编号int out_cnt = 0;//⽤以表⽰出列的⼈数int cnt = n;//表⽰当前圆桌的总⼈数while(out_cnt < n - 1)//当out_cnt等于n-1时,循环结束,此时圆桌师⽣最后⼀个⼈,即我们要的结果{if(cur_index + m > cnt){if((cur_index + m) % cnt == 0)//这种情况需要单独考虑,否则cur_index就变成负值了cur_index = cnt - 1;elsecur_index = (cur_index + m) % cnt - 1;}elsecur_index = cur_index + m - 1;cnt--;out_cnt++;cout<<"当前出列的为:"<<*(rs.begin() + cur_index)<<endl;rs.erase(rs.begin() + cur_index);//从数组中删去需要出队的⼈员}cout<<"最后⼀个出列的⼈物为:"<<rs[0]<<endl;}该⽅法的时间复杂度为O(nm),空间复杂度为O(n),整个算法的基本流程还是⽐较清晰的,相当于每次循环更新cur_cnt、cnt和out_cnt这三个变量,当out_cnt == n-1时,此时出队的⼈数⼀共有n-1⼈,圆桌上只剩下⼀个⼈了,停⽌循环。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构设计——约瑟夫环问题(2008-03-30 15:47:24)转载▼标签:校园第一份目录1. 设计任务书2. 本组课题及本人任务3. 程序功能简介4. 主体内容5. 程序说明6. 操作方法(流程)7. 设计体会8. 附录9. 测试数据10. 附参考书目1.设计任务书1)小组五人根据自己的想法独立编写解决约瑟夫环问题的算法.2) 小组每个人说出自己的算法思想,选择最优算法,编写人负责向其他小组成员解释算法3)分配各自任务,进行开发2.本组课题及本人任务1)课题名:约瑟夫环问题2) 任务:⑴编写算法⑵记录开发日志⑶文档编写⑷程序代码编写⑸调试运行3)小组成员:指导教师:3.程序功能简介1)界面友好2)能完操作方法(流程)3)执行报数,储存出列人的序号,删除出列人的信息以及把出列人的密码赋给m;4)输出正确的排列顺序4.主体内容设计分析:基本思想通过我们对约瑟夫问题的分析,我们认为利用单向循环链表就能解决这个问题。
因为约瑟夫环的大小有输入决定,所以要进行动态内存分配。
为了实现上述操作,应以单向循环链表为存储结构。
new_code( )操作结果:构造空链表,若成功就初始化每个人的相关信息delete_code( )初始条件:线性链表存在操作结果:释放指向出列的人的结点,并重新报数本程序包含三个模块:⑴主程序模块;⑵构造链表并输入每个人信息模块;⑶释放结点模块;5.程序说明本程序完成的是:编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数).一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数.报M的人出列,将他的密码作为新的M值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止.程序输出出列顺序.6.操作方法(流程)1) 打开VC++ 6.0中文版。
2) 输入自己编写好的源程序代码。
3)运行程序,并找出程序中的错误,加以改正,直到能成功运行4)输入测试数据,分析实验结果通过约瑟夫环算法的课题设计让我理解了循环队列,不单单只是书本上文字的循环队列的概念,更多是自己能够通过实际的操作对循环队列有了更深的了解。
上机的编程的过程是对数据结构的基础的进一步的巩固。
学习过程体验到了学习的乐趣,实验课题使我认识到平时学习的漏洞和知识的缺乏,为以后的学习敲了一下警钟,数据结构是门基础,要学习扎实才行数据结构是在整个计算机科学与技术领域上广泛被使用的术语。
它用来反映一个数据的内部构成,即一个数据由那些成分数据构成,以什么方式构成,呈什么结构。
数据结构有逻辑上的数据结构和物理上的数据结构之分。
逻辑上的数据结构反映成分数据之间的逻辑关系,而物理上的数据结构反映成分数据在计算机内部的存储安排。
数据结构是数据存在的形式。
数据结构是信息的一种组织方式,其目的是为了提高算法的效率,它通常与一组算法的集合相对应,通过这组算法集合可以对数据结构中的数据进行某种操作。
数据结构课程的主要目的是介绍一些常用的数据结构,阐明数据结构内在的逻辑关系,讨论它们在计算机中的存储表示,并结合各种数据结构,讨论对它们实行的各种运算的实现算法。
很多算法实际上是对某种数据结构施行的一种变换,研究算法也就是研究在实施变换过程中数据结构的动态性质。
学习的过程需要合作,而且在合作中提到自己的编程水平,借鉴他人好的地方,改掉原先自己不足,书本知识的与实际的联系,使自己的编程不在局限于原来的纸上谈兵,更多的是积累了经验,培养了能力8.附录1)程序中主要变量、函数(指自定义函数及程序中用到的部分不常用系统函数)列表(含简要说明);实现主要功能的(本人任务部分)核心源程序#include<iostream.h>#include<string.h>typedef struct List{bool IsOn;int num;int psw;List*pnext;}*PList;enumStatus{ERROR_OK=0,ERROR_MEMORY_FALIED,ERROR_INPUT_NUM,ERROR_NOPER SON,ERROR_NUM};void GetErrorMsg(const Status &errorCode){char msg[256];switch(errorCode){case ERROR_OK:strcpy(msg,"执行成功!");break;case ERROR_MEMORY_FALIED:strcpy(msg,"内存错误:没有足够大的内存!");break;case ERROR_INPUT_NUM:strcpy(msg,"输入数据错误!");break;case ERROR_NUM:strcpy(msg,"数据错误!");break;case ERROR_NOPERSON:strcpy(msg,"错误:此编号的人不存在!"); break;default:strcpy(msg,"未知错误!");}cout<<msg<<endl;}Status CreateList(PList&pls,const int num) {if(num>30||num<=0)return ERROR_NUM;PList pTemp=new List;pls=pTemp;pls->num=1;pls->IsOn=true;for(int i=1;i<num;i++){pTemp->pnext=new List;if(pTemp==NULL)return ERROR_MEMORY_FALIED;pTemp=pTemp->pnext;pTemp->num=i+1;pTemp->IsOn=true;pTemp->pnext=NULL;}pTemp->pnext=pls;return ERROR_OK;}Status InputPassWord(PList&pls,const int num) {if(num<=0)return ERROR_INPUT_NUM;PList pTemp=pls;for(int i=1;i!=num;i++){pTemp=pTemp->pnext;}if(num==i){cin>>pTemp->psw;if(pTemp->psw<=0)return ERROR_INPUT_NUM;elsereturn ERROR_OK;}elsereturn ERROR_INPUT_NUM;}Status ShowResult(const PList&pls,const int &m){if(m<=0)return ERROR_INPUT_NUM; if(pls==NULL)return ERROR_NUM;int num=0;PList pTemp=pls;do{num++;pTemp=pTemp->pnext;}while(pTemp!=pls);int i=0,j,ps=m;while(i!=num){if(i==0)ps--;for(j=0;j<ps;j++){do{pTemp=pTemp->pnext;}while(!pTemp->IsOn);}pTemp->IsOn=false;cout<<"第"<<i+1<<"个出列的编号为:"<<pTemp->num<<endl; ps=pTemp->psw;i++;}return ERROR_OK;//返回成功}void main(){cout<<"*************************************"<<endl <<"*******欢迎使用约瑟夫环模拟程序******"<<endl<<"*************************************"<<endl<<endl;cout<<"****************数据输入*************"<<endl <<"*************************************"<<endl; PList pls=NULL;int iTemp;Status errorCode;cout<<"**********Step1:环中人数输入*********"<<endl; do{cout<<"请输入模拟环中的人数:";cin>>iTemp;errorCode=CreateList(pls,iTemp);GetErrorMsg(errorCode);}while(!(ERROR_OK==errorCode));cout<<"************Step2:密码输入***********"<<endl; for(int i=1;i<=iTemp;i++){cout<<"请输入第"<<i<<"个人的密码:";errorCode=InputPassWord(pls,i);while(errorCode!=ERROR_OK){GetErrorMsg(errorCode);cout<<"请输入第"<<i<<"个人的密码:";errorCode=InputPassWord(pls,i);}}cout<<"**********Step3:初始密码输入*********"<<endl;do{cout<<"请输入初始密码:";cin>>iTemp;cout<<"**********程序执行结果***********"<<endl<<"**********************************"<<endl;errorCode=ShowResult(pls,iTemp);GetErrorMsg(errorCode);}while(ERROR_OK!=errorCode);}9.测试数据:当n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6,则这正确的出列顺序为6,1,4,7,2,3,510.附参考书目清华大学出版社《数据结构(C语言版)》数据结构——习题与解析,李春葆,清华大学出版社。