【案例】约瑟夫环问题

合集下载

c语言经典例题古老问题的解决

c语言经典例题古老问题的解决

c语言经典例题古老问题的解决以下是一个经典的C语言例题,它是关于解决一个古老的问题:约瑟夫环问题。

约瑟夫环问题是一个著名的数学和计算机科学问题,其描述如下:有n个人围成一圈,从第k个人开始报数,数到m的人出圈,然后从下一个人开始重新报数,数到m的人再出圈,直到剩下最后一个人。

求最后留下的人在原序列中的编号。

以下是一个使用C语言解决这个问题的示例代码:```cinclude <>include <>int main() {int n, k, m;printf("请输入人数n:");scanf("%d", &n);printf("请输入开始报数的位置k:");scanf("%d", &k);printf("请输入数到m的人出圈的数m:");scanf("%d", &m);int a = (int )malloc(n sizeof(int));for (int i = 0; i < n; i++) {a[i] = i + 1;}int index = k - 1; // 初始位置为第k个人while (n > 1) {for (int i = 0; i < n; i++) {index = (index + m - 1) % n; // 计算下一个要出圈的人的位置printf("出圈的人是:%d\n", a[index]);free(a[index]); // 释放该位置的内存n--; // 人数减1}if (index >= k) { // 如果最后一个出圈的人的位置大于等于k,则交换位置,保证下次从第k个人开始报数int temp = a[index];a[index] = a[0];a[0] = temp;}index = (index + 1) % n; // 重新开始报数}printf("最后留下的人是:%d\n", a[0]); // 最后留下的人即为所求结果 free(a); // 释放整个数组的内存return 0;}```该程序首先要求用户输入人数n、开始报数的位置k和数到m的人出圈的数m,然后创建一个长度为n的数组a,将数组中的元素初始化为1到n 的整数。

约瑟夫环问题(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个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。

(H)实验1 约瑟夫环问题

(H)实验1 约瑟夫环问题
实验1约瑟夫环问题
背景
约瑟夫问题(Josephus Problem)据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
cout<<A[i]<<" "; //输出出去的那个人
for(k=i+1;k<j;k++) //每出去一个人,后面人的序号依次减一
A[k-1]=A[k];
}
cout<<"\n";
return 0;
}
程序运行:
原题:用户输入M,N值,N个人围成一个环,从0号人开始数,数到M,那个人就退出游戏,直到最后一个人求最后一个剩下的人是几号?
问题描述
设编号为1-n的n(n>0)个人按顺时针方向围成一圈.首先第1个人从1开始顺时针报数.报m的人(m为正整数).令其出列。然后再从他的下一个人开始,重新从1顺时针报数,报m的人,再令其出列。如此下去,直到圈中所有人出列为止。求出列编号序列。
基本要求
需要基于线性表的基本操作来实现约瑟夫问题
需要利用数组来实现线性表
输入输出格式
输入格式:n,m
输出格式1:在字符界面上输出这n个数的输出序列
输出格式2:将这n个数的输出序列写入到文件中

约瑟夫环-循环报数问题

约瑟夫环-循环报数问题

约瑟夫环-循环报数问题
约瑟夫游戏的⼤意:30个游客同乘⼀条船,因为严重超载,加上风浪⼤作,危险万分。

因此船长告诉乘客,只有将全船⼀半的旅客投⼊海中,其余⼈才能幸免于难。

⽆奈,⼤家只得同意这种办法,并议定30 个⼈围成⼀圈,由第⼀个⼈数起,依次报数,数到第9⼈,便把他投⼊⼤海中,然后再从他的下⼀个⼈数起,数到第9⼈,再将他投⼊⼤海中,如此循环地进⾏,直到剩下 15 个游客为⽌。

问:哪些位置是将被扔下⼤海的位置?
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, m,i,s=0;
cin>>n; //总⼈数
cin>>m; //m个⼀个报数循环(注意参数下标问题)
for(i=2;i<=n;i++)
s=(s+m)%i;
printf("%d", s+1);
return 0;
}。

约瑟夫问题的经典三个例子

约瑟夫问题的经典三个例子

约瑟夫问题的经典三个例子以下是 9 条关于约瑟夫问题的经典例子:例子 1:想想看,一群小朋友围成一圈玩游戏,就像我们小时候那样。

这时候说从某个小朋友开始报数,每隔一个人淘汰,最后剩下的那个就是胜利者。

这不就是约瑟夫问题嘛。

就好像在一个神秘的游戏圈子里,大家都紧张又兴奋地等待着命运的裁决。

例子 2:你能想象军队里士兵们站成一圈,然后用这种方式来决定谁去执行特殊任务吗?哎呀呀,那场面肯定很刺激。

每个士兵心里都七上八下的,不知道自己是不是那个“幸运儿”,这和约瑟夫问题如出一辙。

例子 3:假如在一场盛大的聚会中,大家玩这样的游戏,是不是超级有趣?就像一个魔法圈,把大家的注意力都吸引过来了。

每淘汰一个人,大家就会一阵惊呼,这不正是约瑟夫问题带来的独特体验嘛。

例子 4:你看过那种生存挑战节目吗?选手们围成一圈,然后通过类似约瑟夫问题的规则来淘汰人。

哇塞,那紧张的氛围,可不就是在经历一场残酷的竞争,这就是约瑟夫问题在现实中的精彩呈现呀!例子5:好比一群探险家在荒岛上,为了分配重要资源而采取这种方式。

每个人都祈祷自己不要被先淘汰掉,这种感觉是不是很奇妙?这就是约瑟夫问题带来的不确定性啊。

例子6:想象一下公司团建的时候玩这个,大家既期待又担心。

“哎呀,可别先轮到我呀!”“哇,我居然留下来了。

”这种种反应,不就是约瑟夫问题的魅力所在吗?例子 7:学校运动会上,各班学生围成一圈进行比赛,多刺激呀!有人欢喜有人忧,这不就是约瑟夫问题所引发的情绪波澜吗?例子 8:在一个神秘的魔法学院里,学生们也用这种方式来选拔优秀学员。

每一个人都全神贯注,这和约瑟夫问题一样充满了悬念呢!例子9:如果在一个古老的部落中,用约瑟夫问题来决定首领的继承人,那该是多么惊心动魄的场面啊。

大家的心都提到了嗓子眼,。

【待解惑问题(已解决)】约瑟夫(Josephus)环问题

【待解惑问题(已解决)】约瑟夫(Josephus)环问题

【待解惑问题(已解决)】约瑟夫(Josephus)环问题周末到了,⾸先祝⼤家周末愉快。

可怜我周末都没得休息,继续努⼒:)⼤家该哈⽪的去哈⽪吧。

昨天,其实从前天开始就开始准备约瑟夫环,昨晚就准备开始写,不过想来想去对其数学推导出递推式还是没有想明⽩。

⼤家有空帮忙看看,到底这个递推式是怎么出来的呢?我下⾯会⼀步步将⾃⼰的思考列下来。

多谢多谢。

⼀开始将题⽬和模拟法稍微写⼀下,之后是关于数学⽅法的疑惑,⼤家可以直接略过上⾯的⼀些内容,跳到关键部分。

【题⽬】约瑟夫环问题,每⼀个学习过数据结构或者C语⾔的⼈应该都会遇到过的问题。

不论是在课本的教授内容中,还是在习题⾥⾯,都应该会出现过它的⾝影。

基本的题⽬内容如下:n个⼈围成⼀圈,编号从1到n,报数1,2,3,从编号为1的⼈开始报数,报到3的离开,下⾯的⼈接着从1开始报数,依次下去,写程序求最后剩下来的⼀个⼈编号是⼏。

这⾥题⽬中报数为到3为⽌,其实可以扩展到m。

也就是n个⼈,报到m的⼈出列,同时也可以扩展到以某⼀个⼈为开始,⽽并不是从第1个⼈开始。

【模拟法】⼀般在数据结构课上遇到这个问题的时候,是讲解循环链表的时候,这个题⽬是⽤来检验循环链表学习的结果的。

所以⼀开始我们⽤最“朴素”的⽅法来解决。

⼀般这种⽅法,被称之为模拟法,也就是模拟问题发⽣的过程,直到得到最后的结果,这种解法是最容易想到的,但是⼀般也是效率最低的。

使⽤模拟法来解决,我们可以使⽤循环链表,也可以使⽤数组来模拟循环。

这⾥我们使⽤循环链表,循环链表尽管⽐数组要写得⿇烦,但是思考起来简单直观。

解决问题的核⼼步骤如下:1.建⽴⼀个循环链表,将每个节点进⾏编号2.开始报数,当报到3,删除该节点,然后接着报数3.直到只剩下最后⼀个节点的时候结束,将该节点的值输出Code⽤数组的话,就是要实现循环,也就是当数到n的时候,不会再进⾏n+1,⽽是从1再开始。

这个我们可以⽤取余数来得到。

开始模拟,⾸先第⼀个退出的⼈肯定是m%n,然后需要将之后的位置进⾏调整,(是否可以不进⾏调整?⽤标志位似乎也不是特别好)此时如果还要数组循环的话,就需要与n-1进⾏取余操作。

线性表的应用 约瑟夫环

线性表的应用 约瑟夫环
3 , 1, 7, 2, 4, 8
数据模型 由上述分析可得: 数据模型----线性表 把每个人的编号看成是一个数 据元素,n个编号构成一个线性表
线性表的应用--约瑟夫环 (Josephu环) 【分析问题】 设:M的初值为3;n=6,6个人的密码依次为:
3 , 1, 7, 2, 4, 8
存储结构设计 约瑟夫环问题本身具有循环性质,采用循环单链表
3 , 1, 7, 2, 4, 8
算法步骤
1.工作指针pre和p初始化,计数器count初始化 Pre=head;p=head->next; count=2 2.循环直到p=pre 2.1 如果count=m,则 2.1.1 输出结点p的编号和密码 2.1.2 新的m=当前人的密码 2.1.2 删除结点p 2.1.3 计数器count=1,重新开始计数。 2.2 否则, 2.2.1 工作指针pre和p后移 2.2.2 计数器增1 2.3 链表中只剩下一个结点P,输出结点p后将结点p删除
线性表的应用--约瑟夫环 (Josephu环) 【分析问题】 设:M的初值为3;n=6,6个人的密码依次为:
3 , 1, 7, 2, 4, 8
算法设计
head
1
2
Count=2 p
3
4
5
6
6
pre
为了便于删除操作,从2开始计数
线性表的应用--约瑟夫环 (Josephu环) 【分析问题】 设:M的初值为3;n=6,6个人的密码依次为:
数据模型数据模型线性表把每个人的编号看成是一个数据元素n个编号构成一个线性表线性表把每个人的编号看成是个数线性表的应用约瑟夫环josephu环设
线性表的应用
约瑟夫环 (Josephu环)

趣学算法——约瑟夫环问题(java版)

趣学算法——约瑟夫环问题(java版)

趣学算法——约瑟夫环问题(java版)1 什么是约瑟夫环问题?约瑟夫,是⼀个古犹太⼈,曾经在⼀次罗马叛乱中担任将军,后来战败,他和朋友及另外39个⼈躲在⼀⼝井⾥,但还是被发现了。

罗马⼈表⽰只要投降就不死,约瑟夫想投降,可是其他⼈坚决不同意。

怎么办呢,他想到⼀个主意:让41个⼈围成⼀个圆圈,从第⼀个⼈开始报数,数到3的那个⼈被旁边的⼈杀死。

这样就可以避免⾃杀了,因为犹太⼈的信仰是禁⽌⾃杀的。

结果⼀群⼈杀来杀去最后只剩下两个了,就是约瑟夫和他朋友,于是两⼈愉快地去投降了。

约瑟夫和朋友站在什么位置才保住了性命呢,这就是我们今天要讲的约瑟夫环问题。

2 问题的重要性这是个BAT常⽤⾯试题,⽽且本质上是⼀个游戏,可以⼴泛应⽤于⽣活中,⼯作⽣活好帮⼿就是它了。

3 约瑟夫环抽象问题这个问题实际在讲:N个⼈围成⼀圈,第⼀个⼈从1开始报数,报M的被杀掉,下⼀个⼈接着从1开始报,循环反复,直到剩下最后⼀个,那最后胜利者的初始位置在哪⾥?模拟流程:假如有5个⼈报数,报到3被杀,情况如下A B C1 D E (初始位置,C第⼀个被杀)D E A2 B (C死后的第⼆次排位,A第⼆个被杀)B D E3 (A死后的第三次排位,E第三个被杀)B4 D (E死后的第四次排位,B第四个被杀)D (D留在了最后,初始位置是4)解决⽅法:1 循环遍历法public static int josephus(int n, int m) {//n个⼈, 0 1 2..n-1int[] people = new int[n];//⼈的索引int index = -1;//报数记录, 1 2 3..mint count = 0;//剩余⼈数初始值为nint remain = n;//为了找到最后⼀个幸存者的位置,假设所有⼈都会被杀while (remain > 0) {index++; //找到报数的⼈if (index == n) { //所有⼈遍历⼀圈后从头遍历index = 0;}if (people[index] == -1) { //如果当前的⼈被杀跳过continue;}count++; //报数if (count == m) {people[index] = -1; //报数到m后杀⼈count = 0; //报数重置remain--; //剩余⼈数递减}}return index;}将41传⼊⽅法后,可得结果为30, 因为是从0开始计数,所以等价于现实世界的第31位。

约瑟夫环问题(Josephus)-文档资料

约瑟夫环问题(Josephus)-文档资料
总人数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
问题描述
• 已知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
算法设计
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]+" "); } }
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。

循环队列之约瑟夫环问题

循环队列之约瑟夫环问题

循环队列之约瑟夫环问题约瑟夫问题 约瑟夫环(约瑟夫问题)是⼀个数学的应⽤问题:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。

从编号为k的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列。

通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。

循环队列求解(链式)#include<stdio.h>#include<stdlib.h>//循环队列//typedef int ElemType;typedef struct QueueNode{int data;struct QueueNode *next;}QueueNode;typedef struct Queue{QueueNode *front;QueueNode *rear;}Queue;void InitQueue(Queue *q){q->front=q->rear=NULL;}void EnQueue(Queue *q , int value){QueueNode *temp=(QueueNode*)malloc(sizeof(QueueNode));temp->data=value;if(q->rear==NULL){temp->next=temp;q->rear=q->front=temp;}else{temp->next=q->rear->next;q->rear->next=temp;q->rear=temp;}}//enter a element from the tailvoid DeQueue(Queue *q, int *value){QueueNode *temp=(QueueNode*)malloc(sizeof(QueueNode)); if(q->rear==NULL){return;}// It's nullelse if(q->rear->next==q->rear){*value=q->front->data;free(q->rear);q->rear=q->front=NULL;}//It just has one nodeelse{*value=q->front->data;temp=q->front;q->front=temp->next;q->rear->next=q->front;}//more one nodefree(temp);}//delete a element from the headint main(){Queue *q=(Queue*)malloc(sizeof(Queue));int i,m,n,count,temp;printf("请输⼊⼈数n和循环要报的数m(两数之间留个空格)\n"); scanf("%d%d",&n,&m);for(i=1;i<=n;i++)EnQueue(q,i);printf("出圈序列:\n");while(q->front){ count=1;while(count<m){q->front=q->front->next;q->rear=q->rear->next;count++;}count=1;DeQueue(q,&temp);printf("%d ",temp);}putchar('\n');}简单解法#include <stdio.h>int josephus(int n, int m) {if(n == 1) {return0;}else {return (josephus(n-1, m) + m) % n;}}int main() {int n, m;while (scanf("%d", &n) == 1) {if (!n) {break;}scanf("%d", &m);int result = josephus(n, m);printf("%d\n", result+1);}return0;}。

约瑟夫环实验报告

约瑟夫环实验报告

约瑟夫环实验报告约瑟夫环(Josephus problem)是一个非常经典的数学问题,其得名于公元1世纪的犹太历史学家约塞夫斯(Josephus)。

约瑟夫环问题描述如下:n个人围坐成一个圆圈,从一些人开始依次报数,每报到第m个人,该人就被淘汰出圆圈,然后从下一个人重新开始报数。

直到剩下最后一个人时,即为问题的解。

例如,当n=7,m=3时,最后剩下的是4号人。

本次实验的目的是研究约瑟夫环问题的解决方法,并通过编程实现给定n和m的情况下找到最后的获胜者。

首先,我们需要分析问题的特点。

当n=1时,该问题的解即为最后剩下的人;当n>1时,最后剩下的人可以通过前一轮问题的解(剩下n-1个人的情况下)推导出来。

我们可以将解决该问题的方法分为两种:递归法和迭代法。

一、递归法递归法是通过问题的子问题来解决原问题。

对于约瑟夫环问题来说,递归法的解题思路如下:1.当n=1时,问题的解即为1;2.当n>1时,问题的解为(找到n-1个人时的解+m-1)对n取模,即((f(n-1,m)+m-1)%n)+1二、迭代法迭代法通过循环来解决问题,不断更新当前的解,直到问题得到解决。

对于约瑟夫环问题来说,迭代法的解题思路如下:1.初始化一个长度为n的数组a,a[i]=1表示第i个人还在圆圈中,a[i]=0表示第i个人已经被淘汰出圆圈;2. 从第一个人开始计数,每报数到第m个人,则将该人设为已淘汰,并计数器count加1;3. 重复步骤2,直到count=n-1;4.循环遍历数组a,找到最后剩下的人。

为了更加直观地展示实验结果,我们通过Python编写下述代码:```python#递归法解决约瑟夫环问题def josephus_recursive(n, m):if n == 1:return 1else:return (josephus_recursive(n - 1, m) + m - 1) % n + 1#迭代法解决约瑟夫环问题def josephus_iterative(n, m):a=[1]*ncount = 0i=0while count < n - 1:if a[i] == 1:j=0while j < m:if a[(i + j) % n] == 1:j+=1else:j=0i=(i+1)%na[(i-1)%n]=0count += 1for i in range(n):if a[i] == 1:return i + 1#测试递归法解决约瑟夫环问题print(josephus_recursive(7, 3)) # 输出4 #测试迭代法解决约瑟夫环问题print(josephus_iterative(7, 3)) # 输出4 ```通过以上代码,我们可以得到n=7,m=3时,最后剩下的人是4号人。

【案例】约瑟夫环问题

【案例】约瑟夫环问题

修改
void void void void baoshu(Josephus *, int n, init_Josephus(Josephus *, display_Josephus(Josephus sort_by_count(Josephus *, int m); int n); *, int n); int n);
各项功能分别用函数实现
// 模拟报数 void baoshu(Josephus man[], int n, int m);
// 初始化 void init_Josephus(Josephus man[], int n);
// 输出结构体数组 void display_Josephus(Josephus man[], int n); // 按出列顺序排序 void sort_by_count(Josephus man[], int n);
// 初始化 void init_Josephus(Josephus man[N], int n) { int i; for(i=0;i<n;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; //保存出列的序号,为0表示未出列 } } // 输出结构体数组 void display_Josephus(Josephus man[], int n) { int i; printf("约瑟夫排列(最初位置--姓名--约瑟夫环位置):\n"); for(i=0;i<N;i++) { printf("%d--%s--%d\n",man[i].num ,man[i].name,man[i].count ); } }

约瑟夫环问题(最简单的数学解法)

约瑟夫环问题(最简单的数学解法)

约瑟夫环问题(最简单的数学解法)基本问题描述:已知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即可。

约瑟夫环问题 实验报告完整版

约瑟夫环问题 实验报告完整版
}
(2)约瑟夫环报数的算法在运行为循环方式,报数者除非本身已经出去,否则继续顺序报数,其报数循环的代码为
void Joseph(NODE *p,int number,int n)
{
int;=number; i++)
{
for(j=1; j<n-1; j++)
(2)基本要求
建立模型,确定存储结构。
对任意n个人,密码为m,实现约瑟夫环问题。
出圈的顺序可以依次输出,也可以用一个数组存储。
(3)思考:
采用顺序存储结构如何实现约瑟夫环问题?
如果每个人持有的密码不同,应如何实现约瑟夫环问题?
2.数据结构设计
由于约瑟夫环问题本身具有循环性质,考虑采用循环链表,为了统一对表中任意结点的操作,循环链表不带头结点。将循环链表的结点定义为如下结构类型:
5.运行测试与分析
(1)输出提示,如图1.2所示。
(2)根据提示,输入圈内人数n和每个人持有的密码m如图1.3所示。
(3)输出结果如图1.4所示
分析
6.实验收获及思考
通过该实验,我进一步增强了对于链表的理解,也对链表的操作和实现更为熟悉,熟练掌握了如何实现置空表、求表的长度、取结点、定位运算、插入运算、删除运算、建立不带头结点的单链表(头插入法建表)、建立带头结点的单链表(尾插入法建表),输出带头结点的单链表等操作。同时,锻炼了实际操作时的动手能力。
{
p=p->next;
}
q=p->next;
p->next=q->next;
p=p->next;
printf("第%3d个出圈的人是:%3d\n",i,q->value);

约瑟夫环问题

约瑟夫环问题

1.约瑟夫环问题(实验类型:综合型)1)问题描述:有编号为1, 2…n 的 n 个人按顺时针方向围坐一圈,每人持有一个正整数密码。

开始给定一个正整数 m,从第一个人按顺时针方向自1开始报数,报到m者出列,不再参加报数,这时将出列者的密码作为m,从出列者顺时针方向的下一人开始重新自1开始报数。

如此下去,直到所有人都出列。

试设计算法,输出出列者的序列。

2)实验要求: 采用顺序和链式两种存储结构实现3) 实现提示:✧用顺序表来存储围座者的序号和密码(顺序存储结构).⏹用number 和code分别表示围座者的序号和密码.假设围座者人数为j,当前使用密码为m,开始报数者位置为s, 那么下一出列者位置为s=(s+m-1) mod j.⏹当我们要在线性表的顺序存储结构上的第i个位置上插入一个元素时,必须先将线性表的第i个元素之后的所有元素依次后移一个位置,以便腾空一个位置,再把新元素插入到该位置。

若要删除第i个元素时,也必须把第i个元素之后的所有元素前移一个位置。

✧用链式存储解决此问题时可以采用循环链表.4)注意问题:顺序存储和链式存储实现此算法时计算出列位置的不同方法,人员出列后所做操作的区别。

#include<stdio.h>#include<stdlib.h>typedef struct Node{int key;//密码int num;//编号struct Node *next;}Node,*Link;void InitList(Link &L){//创建空链表L=(Node*)malloc(sizeof(Node));if(!L)exit(1);L->key=0;L->next=L;}void Creatlinklist(int n,Link&L){//初始化链表Link p,q;q=L;for(int i=1;i<=n;i++){p=(Node*)malloc(sizeof(Node));if(!p)exit(1);scanf("%d",&p->key);p->num=i;L->next=p;L=p;}L->next=q->next;free(q);}Link Locate_m(Link &p,int m){//找到第m个Link q;for(int j=1;j<m;j++)p=p->next;q=p->next;m=q->key;return q;}void Delete_m(Link &L,Linkp,Link q){//删除第m个p->next=q->next;free(q);}int main(){Link L,p,q;int n,m;L=NULL;InitList(L);//构造一个只有头节点的空链表printf("输入初始密码:");scanf("%d",&m);//初始密码为m printf("输入总人数:");scanf("%d",&n);//总人数为nprintf("输入每个人手中的正整数密码:");Creatlinklist(n,L);//建立约瑟夫环p=L;printf("出列者顺序为:") ;for(int i=1;i<=n;i++){q=Locate_m(p,m);//找到第m个printf("%d ",q->num);Delete_m(L,p,q);//删除第m个}}5)实验心得:大部分的时间都用在了编程上,主要是因为C语言掌握的问题,C语言基础不好特别是对于C语言中链表的一些定义和基本操作不够熟练,导致在编程过程中还要不断的拿着c语言的教材查找,所以今后还要对C语言多练习,多动手,多思考。

约瑟夫问题及变种

约瑟夫问题及变种

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

约瑟夫环

约瑟夫环
2.确定第1个报数人的位置;
3.不断地从链表中删除链结点,直到链表为空。
void JOSEPHUS(int n,int k,int m) //n为总人数,k为第一个开始报数的人,m为出列者喊到的数
{
/* p为当前结点 r为辅助结点,指向p的前驱结点 list为头节点*/
}
这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。
参照上面提供的思路,我认为可以类似的得到一个更易于明白的方法,设有(1,2,3,……,k-1,k,k+1,……,n)n个数,当k出列时,那么有
LinkList p,r,list;
/*建立循环链表*/
for(int i=0,i<n,i++)
{
p=(LinkList)malloc(sizeof(LNode));
p->data=i;
if(list==NULL)
list=p;
else
r->link=p;
{
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\n", s+1);
return 0 ;
f=(f[i-1]+m)%i; (i>1)
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

int main()
{
Josephus man[N]; init_Josephus(man,N);
baoshu(man, N, M) ;
printf("\n按座位顺序排列:\n"); display_Josephus(man,N); sort_by_count(man,N); printf("\n按出列顺序排列:\n"); display_Josephus(man,N); getch(); return 0;
模拟循环报数
i=0; while(counter<=N) //在N个人中模拟循环报数 { do{ pos=(pos+1) % N; //求余,进行环状处理 if( man[pos].count==0) //若编号pos还未出列 i++; //报数 if(i==M) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos].count =counter; //保存出列序号 counter++; }
修改
void void void void baoshu(Josephus *, int n, init_Josephus(Josephus *, display_Josephus(Josephus sort_by_count(Josephus *, int m); int n); *, int n); int n);
方法1:使用一维数组
• 用一个一维整型数组保存约瑟夫环
• 数组元素的序号(下标)表示人员的座位序号
• 数组元素的值表示出列的序号 如,man[0]=14 表示排在第1个位置的人将是第14个出列的人
#define N 41 #define M 3 int int int int
//总人数 //数到3出列
// 模拟报数 void baoshu(Josephus man[N], int n, int m) { int counter=1; //出列记数器 int i=0; //报数记数器 int pos=-1; //位置记数器 while(counter<=n) //在N个人中模拟循环报数 { do{ pos=(pos+1) % n; //求余,进行环状处理 if(man[pos].count==0) //若编号pos还未出列 i++; //报数 if(i==m) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos].count=counter; //保存出列序号 counter++; } }析
什么是约瑟夫环问题
• 传说,著名犹大历史学家Josphus曾讲过一个故事: 在罗马人占领乔塔帕特后,40个犹太人与Josphus躲到 一个洞中。40个犹大人决定宁愿死也不要被敌人逮到,于是 决定了一个自杀方式:41个人排成一个圆圈,由第1个人开 始报数,每报数到3,该人就必须自杀,然后再由下一个人 重新报数,直到所有人都自杀身亡为止。
// 初始化 void init_Josephus(Josephus man[N], int n) { int i; for(i=0;i<n;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; //保存出列的序号,为0表示未出列 } } void init_Josephus( Josephus *p, int n) 修改 { int i; for(i=0;i<n;i++,p++) { p->num = i+1; gets(p->name); p->count = 0; //保存出列的序号,为0表示未出列 } }
}
方法3:结构体指针
结构体指针作为各函数的形参
void void void void baoshu(Josephus man[], int n, int m); init_Josephus(Josephus man[], int n); display_Josephus(Josephus man[], int n); sort_by_count(Josephus man[], int n);
按出列顺序排序
void sort_by_count(Josephus man[], int n) { int i,j; Josephus t; for(i=0; i<n-1; i++) { for(j=i+1; j<n; j++) { if(man[i].count > man[j].count ) { t=man[i],man[i]=man[j],man[j]=t; } } } }
各项功能分别用函数实现
// 模拟报数 void baoshu(Josephus man[], int n, int m);
// 初始化 void init_Josephus(Josephus man[], int n);
// 输出结构体数组 void display_Josephus(Josephus man[], int n); // 按出列顺序排序 void sort_by_count(Josephus man[], int n);
模拟循环报数
man[N]={0}; //保存出列的序号,为0表示未出列 counter=1; //出列记数器 i=0; //报数记数器 pos=-1; //位置记数器
while(counter<=N) //在N个人中模拟循环报数 { do{ pos=(pos+1) % N; //求余,进行环状处理 if(man[pos]==0) //若编号pos还未出列 i++; //报数 if(i==M) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos]=counter; //保存出列序号 counter++; }
• 排名:根据选手的最后得分排出名次, • 查询:可查询选手信息(按姓名、出场顺序编号或最后名次 等)功能
• 具体功能设计不限于以上。
方法2:结构体数组
如果,还要知道参加游戏人的名字,怎么办? • 座位号:结构体成员(整型) 或 数组的下标 • 名字:结构体成员(字符串) • 出列序号:结构体成员(整型)
// 定义结构体类型 typedef struct { int num; // 座位序号 char name[20]; int count; // 出列序号 }Josephus; // 定义结构体数组 Josephus man[N]; // 结构体数组元素的初始化 for(i=0;i<N;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; }
然而,Josphus并不想遵从自杀,于是他先假装同意该 方案,然后坐到大家围成圆圈的第31个位置,逃过了这场死 亡游戏。
问:如何确定坐在哪个位置上可以逃脱呢?
问题分析
• 将41人排成一个圆圈,并编好序号,如图所示(内圈是座 位序号,外圈是自杀顺序(即,每个报到3的顺序)。
23 14 36 1 38 29 13 15 1 2 3 40 41 4 33 2 39 5 38 6 22 24 37 7 30 12 8 36 9 3 39 35 10 16 28 34 11 34 11 33 12 4 21 32 13 25 31 14 30 17 10 15 29 5 32 16 28 20 40 17 27 26 18 9 19 31 25 20 24 23 27 21 22 6 35 18 8 19 37 7 26
// 初始化 void init_Josephus(Josephus man[N], int n) { int i; for(i=0;i<n;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; //保存出列的序号,为0表示未出列 } } // 输出结构体数组 void display_Josephus(Josephus man[], int n) { int i; printf("约瑟夫排列(最初位置--姓名--约瑟夫环位置):\n"); for(i=0;i<N;i++) { printf("%d--%s--%d\n",man[i].num ,man[i].name,man[i].count ); } }
大作业:歌手大奖赛竞赛系统
已知某大奖赛有n个选手参赛,m(m>2)个评委为参赛选手 评分(最高100分,最低0分)。 要求编程实现: • 出场顺序:随机从一位选手编号开始,按约瑟夫环的方式,3 人循环报数,决定选手的出场顺序。
• 评委评分:去掉1个最高分和1个最低分后,取平均分作为该 选手的最后得分。
相关文档
最新文档