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

发布时间:2020-06-22 23:26:48 作者:张立达
来源:网络 阅读:227

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     else145         printf("Survival: %d\n",(answer + id) % number);  
146         // 这边利用number对answer进行取余操作以防止编号数值超过最大编号(溢出)147     148     return 0;149 }

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

                                           对于Josephus问题有两个地方是可以进行优化的。 (总人数为N,编号为从0~N-1;经过M次报数去除一个成员,剩余成员个数为numleft, 记M%numleft为mPrime)

 1、被移除的成员离上一个成员之间的距离是M%numleft-1(报数次为M%numleft).当M大于N时,该计算方式将节省大量时间
 2、当mPrime大于numleft的时候可以反向遍历该表来查找要去除的成员。这样可以节省时间。同样这也就要求了该表必须是一个双向表才行。(即含有Previous方法)
  该算法实现原理即为:

  第一轮,必定为编号M%N-1的成员被去除,第二轮为在第一轮的基础上即从编号为M%N的成员开始正移mPrime-1个单位(或者反移numleft-mPrime-1个单位)。若将M%N即为编号0,开始重新编号,那么第二轮被删除的成员编号便是M%(numleft)-1,由此可得该轮要被删除的成员与上一轮去除成员之间的距离为M%numleft,这里可利用迭代器来实现。

  这里我们便可以得到成员编号与该轮成员数目的关系是:n表示该轮所剩余的成员数目,Index(n)表示该轮成员的编号(从0开始)
   Index(n) = (Index(n - 1) + m) % n。
    那么按照这个过程,我们这样一直移除元素下去,肯定能够找到最后一个被移除的元素。
    这个元素则对应只有一个元素的环,很显然,它的值为0。也就是Index(1) = 0。
    对于这个元素的索引,它对应两个元素的索引是多少呢?
   按照前面的过程,我们倒推回去就是了。Index(2) = (Index(1) + m) % 2。
   那么对应3个,4个元素的呢?我们这样一路继续下去就可以找到对应到n个元素的索引了。
    所以,我们发现了一个有意思的数学归纳关系:
    f(1) = 0,  f(n) = (f(n - 1) + m) % n。
    按照这个关系,我们可以得到最后一个被取出来的元素对应到n个元素的环里的索引值。 

至此,我们可以发现,利用count计数从而删除成员的方法与此相比起来逊色不少,故之后我们将采用此方法来解决问题。


推荐阅读:
  1. Java问题定位方法总结
  2. 在安装PHPadmin的过程与遇到的问题及解决方法总结

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

count j hu

上一篇:HBase基础操作,包括表的增删改查过滤等

下一篇:简单安全的电脑文件加密方案?如何控制数据文档随意外发?选择湖

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》