约瑟夫环问题

来源:互联网 发布:js更改div高度 编辑:程序博客网 时间:2024/05/17 22:30
问题描述:
      约瑟夫环问题(Josephus)
      用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。(约瑟夫环问题 Josephus)

解法一(My Solution):
      思想:建立一个有N个元素的循环链表,然后从链表头开始遍历并记数,如果计数i==m(i初始为1)踢出元素,继续循环,当当前元素与下一元素相同时退出循环。
代码:
/* 约瑟夫环问题(Josephus) 用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。(约瑟夫环问题 Josephus) Code By Eric Yang 2009 http://ericyang.cnblogs.com*/#include <stdio.h>#include <stdlib.h>// 链表节点typedef struct _RingNode{    int pos;  // 位置    struct _RingNode *next;}RingNode, *RingNodePtr;// 创建约瑟夫环,pHead:链表头指针,count:链表元素个数void CreateRing(RingNodePtr pHead, int count){    RingNodePtr pCurr = NULL, pPrev = NULL;    int i = 1;    pPrev = pHead;    while(--count > 0)    {        pCurr = (RingNodePtr)malloc(sizeof(RingNode));        i++;        pCurr->pos = i;        pPrev->next = pCurr;        pPrev = pCurr;    }    pCurr->next = pHead;  // 构成环状链表}void PrintRing(RingNodePtr pHead){    RingNodePtr pCurr;    printf("%d", pHead->pos);    pCurr = pHead->next;    while(pCurr != NULL)    {        if(pCurr->pos == 1)            break;        printf("\n%d", pCurr->pos);        pCurr = pCurr->next;    }}void KickFromRing(RingNodePtr pHead, int m){    RingNodePtr pCurr, pPrev;    int i = 1;    // 计数    pCurr = pPrev = pHead;    while(pCurr != NULL)    {        if (i == m)        {            // 踢出环            printf("\n%d", pCurr->pos);    // 显示出圈循序            pPrev->next = pCurr->next;            free(pCurr);            pCurr = pPrev->next;            i = 1;        }        pPrev = pCurr;        pCurr = pCurr->next;        if (pPrev == pCurr)        {            // 最后一个            printf("\n%d", pCurr->pos);    // 显示出圈循序            free(pCurr);            break;        }        i++;    }}int main(){    int m = 0, n = 0;    RingNodePtr pHead = NULL;    printf("---------------Josephus Ring---------------\n");    printf("N(person count) = ");    scanf("%d", &n);    printf("M(out number) = ");    scanf("%d", &m);    if(n <= 0 || m <= 0)    {        printf("Input Error\n");        system("pause");        return 0;    }    // 建立链表    pHead = (RingNodePtr)malloc(sizeof(RingNode));    pHead->pos = 1;    pHead->next = NULL;    CreateRing(pHead, n);#ifdef _DEBUG    PrintRing(pHead);#endif    // 开始出圈    printf("\nKick Order: ");    KickFromRing(pHead, m);        printf("\n");    system("pause");    return 0;}

解法二(From Net):
      思想:归纳为数学性问题。原文说的很好,还是直接Copy吧,因为搜索半天也没有找到原作者,所以无法添加引用地址了,如果这位大哥看到这里,请告知与我,小弟立刻加入引用链接:)

无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达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     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)

有了这个公式,我们要做的就是从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 ("\nThe winner is %d\n", s+1);}

这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

相比之下,解法二的优越性不言而喻,同时说明数学确实很重要。

转载出处:http://www.cnblogs.com/EricYang/archive/2009/09/04/1560478.html

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 2个月婴儿长太快怎么办 孩子脚长得太快怎么办 4个月宝宝缺钙怎么办 2个月宝宝不长肉怎么办 小孩子长得不高怎么办 宝宝误吃蜂蜜了怎么办 有人拿着吃完的东西退货怎么办 婴儿个子长的慢怎么办 儿童长得太快怎么办 宝宝个子长太快怎么办 孩子九个月奶水不够怎么办 小孩起热痱子痒怎么办 媳妇生完小孩奶水出不来怎么办 生完孩子下奶疼怎么办 生完小孩没奶水怎么办 孩子半个月奶水越来越少怎么办 半个月后奶水越来越少怎么办 坐月子半个月奶水越来越少怎么办 孩子七个月奶水越来越少怎么办 生完孩子奶水越来越少怎么办 生完孩子回奶了怎么办 产妇3天没奶水怎么办 产后7天了奶水少怎么办 刚生完小孩没有奶水怎么办 突然就没奶水了怎么办 生完小孩没有奶水怎么办 生完宝宝没有奶怎么办 剖腹产奶涨的疼怎么办 生产一天了没奶怎么办 第一天断奶 奶水一直流出怎么办 新生儿刚出生没奶水怎么办 安卓手机死机了怎么办 婴幼儿几天不拉大便怎么办 樱桃吃多了胃不舒服怎么办 空腹吃水果胃不舒服怎么办 吃水果后胃不舒服怎么办 吃水果伤胃了怎么办 大人吃退烧药不出汗怎么办 稍微吃点凉水果胃就疼怎么办 1岁宝宝感冒发烧怎么办 3岁小儿反复发烧怎么办