Josephus问题的不同实现方法与总结

  1 /************************************************************************/
  2 /*                   Josephus问题——数组实现                           */
  3 /************************************************************************/
  4 #include <stdio.h>
  5 #include <malloc.h>
  6
  7 int Josephus(int times, int number, int id){
  8     int *a;
  9     int i, count = 0, t = 0;
 10     a = (int *)malloc(sizeof(int) * number);
 11
 12     for(i = 0; i < number; i++)
 13         a[i] = i + 1;             // 数组a用于储存每个元素的编号
 14     i = id - 1;
 15
 16     while(count < number - 1){
 17         if(a[i] != 0)
 18             t++;
 19         if(t == times){
 20             t = 0;
 21             count++;
 22             printf("%4d", a[i]);
 23             a[i] = 0;                // 当该元素被剔除时,该数组元素置为0
 24         }
 25         i++;
 26         if(i == number)
 27             i = 0;
 28     }
 29     for(i=0;i<number;i++)
 30         if(a[i]!=0)
 31         {
 32             printf("\n最后剩余的结点是:%4d\n",a[i]);
 33             return;
 34         }
 35
 36 }
 37
 38 int main(){
 39     int times, number, id;
 40     printf("请输入总人数:");
 41     scanf("%d", &number);
 42     printf("请输入报数周期:");
 43     scanf("%d", &times);
 44     printf("请输入开始报数的编号:");
 45     scanf("%d", &id);
 46     Josephus(times, number, id);
 47
 48     return 0;
 49 }
 50
 51 /************************************************************************/
 52 /* 总结:
 53         优点为可以得出每次被剔除的元素编号
 54         缺点为内存空间占用较大,没有数学归纳法快速                        */
 55 /************************************************************************/
 56
 57
 58 /************************************************************************/
 59 /*                   Josephus问题——循环链表实现                       */
 60 /************************************************************************/
 61 #include <stdio.h>
 62 #include <malloc.h>
 63
 64 typedef struct LNode
 65 {
 66     int data;
 67     struct LNode *next;
 68 }LNode,*Linkhead;
 69 void Josephus(int m,int n,int k)
 70 {
 71     Linkhead p,r,head = NULL;
 72     int i;
 73     for(i = 1;i <= n;i++)
 74     {
 75         p = (Linkhead)malloc(sizeof(LNode));//申请一个新的链结点
 76         p->data = i;//存放第i个结点的编号
 77         if(head == NULL)
 78             head = p;
 79         else
 80             r->next = p;      // 因为Insert和Del操作都需要之前一个节点的地址,故用r来存储。其作用类似栈的top
 81         r = p;
 82     }
 83     p->next = head;//至此,建立一个循环链表
 84
 85     p = head;
 86     for(i = 1;i < k;i++)
 87     {
 88         r=p;
 89         /*请注意,此行不是多余的,因为当k!=1,但m=1时如果没有这条语句,此时删除动作无法完成*/
 90         p=p->next;
 91     }        //此时p指向第1个出发结点
 92
 93     while(p->next != p)
 94     {
 95         for(i = 1;i < m;i++)
 96         {
 97             r = p;
 98             p = p->next;
 99         }                        //p指向第m个结点,r指向第m-1个结点
100         r->next = p->next;        //删除第m个结点
101         printf("%4d",p->data);    //依次输出删除结点的编号
102         free(p);                //释放被删除结点的空间
103         p = r->next;            //p指向新的出发结点
104     }
105     printf("\n最后剩余的结点是:%4d\n",p->data);//输出最后一个结点的编号
106 }
107
108 int main(){
109     int times, number, id;
110     printf("请输入总人数:");
111     scanf("%d", &number);
112     printf("请输入报数周期:");
113     scanf("%d", &times);
114     printf("请输入开始报数的编号:");
115     scanf("%d", &id);
116     Josephus(times, number, id);
117
118     return 0;
119 }
120
121 /************************************************************************/
122 /* 总结:
123         优点为可以得出每次被剔除的元素编号
124         缺点为相较数组方法需要更多的计算量
125         总体而言与数组方法相差无几                                        */
126 /************************************************************************/
127
128 /************************************************************************/
129 /*             Josephus问题——数学归纳法直接计算                       */
130 /************************************************************************/
131 #include <stdio.h>
132 int main() {
133     int answer = 0;
134     int times, number, i, id;    // number为环内总元素个数,times为报数周期, id为从第几个元素开始报数
135     printf("请分别输入总人数和循环次数:");
136     scanf("%d %d", &number, &times);
137     printf("起始报号者的编号:");
138     scanf("%d", &id);
139     for(i = 1; i <= number; i++) {
140         answer = (answer + times) % i;      // 核心算法,利用数学归纳法得出
141     }
142     if(answer + id == number)
143         printf("Survial: %d\n", number);    // 防止当幸存者为最后一个编号时输出0的情况
144     else
145         printf("Survival: %d\n",(answer + id) % number);
146         // 这边利用number对answer进行取余操作以防止编号数值超过最大编号(溢出)
147
148     return 0;
149 }
150
151 /************************************************************************/
152 /* 数学方法:总结归纳法。
153    在一个n长的环里取m的步长,那么这个环里便缺少了一个元素。
154    剩下的n-1个元素构成了n-1环。而这里的元素和n长的元素之间的映射关系是:
155    Index(n) = (Index(n - 1) + m) % n。
156    而如果我们载往下一步移除元素呢,他们之间的关系则是:
157    Index(n - 1) = (Index(n - 2) + m) % (n - 1)。故不难发现其规律,那么
158    按照刚才的过程,我们这样一直移除元素下去,肯定能够找到最后一个被移除的元素。
159    这个元素则对应只有一个元素的环,很显然,它的值为0。也就是Index(1) = 0。
160    对于这个元素的索引,它对应两个元素的索引是多少呢?
161    按照前面的过程,我们倒推回去就是了。Index(2) = (Index(1) + m) % 2。
162    那么对应3个,4个元素的呢?我们这样一路继续下去就可以找到对应到n个元素的索引了。
163    所以,我们发现了一个有意思的数学归纳关系:
164     f(1) = 0,  f(n) = (f(n - 1) + m) % n。
165     按照这个关系,我们可以得到最后一个被取出来的元素对应到n个元素的环里的索引值。
166
167     总结:
168         优点:利用了数学公式推导,算法较为巧妙,计算量和内存占用量相较于其他
169         方法有了很大的优化。
170         缺点:仅仅是针对最终的结果进行计算,故无法得出中间过程                  */
171 /************************************************************************/
时间: 2024-10-13 22:28:52

Josephus问题的不同实现方法与总结的相关文章

Josephus环问题

约瑟夫环问题 问题描述: Josephus问题可以描述为如下的一个游戏:N个人编号从1到N,围坐成一个圆圈,从1号开始传递一个热土豆,经过M次传递后拿着土豆的人离开圈子,由坐在离开的人的后面的人拿起热土豆继续进行游戏,直到圈子只剩下最后一个人.例如:M=0,N=5,则游戏人依次被清除,5号最后留下:如果M=1,N=5,那么被清除的人的顺序是2,4,1,5,最后剩下的是3号. 如下是两种解题方法: 建立一个N大小的数组,存储N个人是否还在圈子内(0为在圈子内,-1为已经离开圈子),依次循环遍历整个

C/C++02: Josephus问题

据说著名犹太历史学家Josephus有过以下的故事: 在罗马人占领乔塔帕特後,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止. 然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏. 用循环列表的数据结构是比较容易实

链表环状检测主要有三种方法

链表环状检测主要有三种方法: 1.追赶法:如 robinzsy. 2.外部记录法:如improgrammer. 3.内部记录法(打记号):如VivianSnow. 内部标记法和外部标记法其实是一个道理,不过就是辅助变量一个是在链表节点内,一个是借助辅助数组或者hash或者AVL,红黑树,把已经访问过的节点地址存起来,每次访问下一个时候做查询处理. 追赶法,利用最大公倍数原理,用2个游标,对链表进行访问,例如:p1,p2, p1访问每步向前进1个节点,p2则每次向前前进2个节点,如果有环则p1,p

约瑟夫问题(Josephus)

问题描述:已知n个人(以编号1,2,3...n分别表示)围成一圈.从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,求最后那个人的编号(直到所有人全部出列,模拟该过程)?解决:此问题可以用数组or链表实现,可以用数学方法进行简化(不用模拟过程时),也可以模拟该问题过程来求解. 数学分析:无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千

Unity 崩溃问题解决方法——之一

友情提示:工作随记,不喜勿喷 注意:文艺青年可以略过,暴力青年欢迎采纳 Library文件夹 Unity每次编译都会生成这个东西,不用担心丢失的问题.所以,干掉他! 选中 + Delete   或者  选中 + Shift  + Delete 网上看了很多方法,就觉得这个简单,粗暴,是我喜欢的类型. 优点:上面说了 缺点:如果工程过大,重新编译时间会比较长.(但是对于查Log日志来说,相对快点)

Java TM 已被阻止,因为它已过时需要更新的解决方法

公司的堡垒机需要通过浏览器登陆,且该堡垒机的网站需要Java的支持,最近通过浏览器登陆之后总是提示"java TM 已被阻止,因为它已过时需要更新的解决方法"导致登陆之后不能操作, 但是操作系统中确实已经安装了比较新的JDK,安装的JDK版本是jdk-7u67-windows-i586,因为太烦人,所以决定搞清楚报错的原因,一劳永逸,彻底解决这个问题 准备工作:安装JDK,安装版本jdk-7u67-windows-i586.exe,因为机器的Eclipse还依赖64位的JDK,所以另安

win7 64位系统 PB连接oracle数据库出现“oracle library oci.dll could not be loaded”问题的解决方法

今天与大家分享一个自己的学习笔记,希望能给遇到同样问题的人带来帮助. 不知道大家在win7 64位系统下用 PB连接oracle数据库时,是否遇到过“oracle library oci.dll could not be loaded”问题. 今天,在win7 64位系统下用 PB连接oracle数据库时,一直出现上述错误,在百度上找了很久,都没有找到一个完整的解决方案,咨询了很多人,(他们都说是我的PB和oracle没装好,但我装的时候没出现任何问题,一切都很顺利,而且PB和oracle都能正

C#与数据库访问技术总结(八)之ExecuteNonQuery方法

ExecuteNonQuery方法 ExecuteNonQuery方法主要用来更新数据. 通常使用它来执行Update.Insert和Delete语句. 该方法返回值意义如下: 对于Update.Insert和Delete语句,返回值为该命令所影响的行数. 对于所有其他类型的语句,返回值为-1. Command对象通过ExecuteNonQuery方法更新数据库的过程非常简单,需要进行的步骤如下: (1)创建数据库连接. (2)创建Command对象,并指定一个SQL Insert.Update

android开发之onCreate( )方法详解

onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Google Android Developers官网上的解释: onCreate(Bundle) is where you initialize your activity. Most importantly, here you will usually call setContentView(int) with a layout resource d