zoukankan      html  css  js  c++  java
  • 约瑟夫环问题的两个方法

      约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

    下面来一个具体的情景:

      有M个敢死队员要炸掉敌人的一碉堡,谁都不想去,排长决定用轮回数数的办法来决定哪个战士去执行任务。如果前一个战士没完成任务,则要再派一个战士上去。现给每个战士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到5时,对应的战士就去执行任务,且此战士不再参加下一轮计数。如果此战士没完成任务,再从下一个战士开始数数,被数到第5时,此战士接着去执行任务。以此类推,直到任务完成为止。指出最后一个去的人。

    方法一:直接用顺序表模拟:

     1 /*===========================================================================*\
     2  *                     约瑟夫环问题
     3  *
     4  *                 方法一:用顺序表直接模拟
     5  *                      
     6  *                   2013-05-20 by 樊列龙
     7  *
     8 \*===========================================================================*/
     9 
    10 #include <stdio.h>
    11 #include <stdlib.h>
    12 #include <malloc.h>
    13 
    14 typedef int ElemType;
    15 
    16 typedef struct         /* 定义数据结构体类型*/
    17 {
    18     ElemType *elem;    /* 存储空间基址*/
    19     int length;        /* 当前长度*/
    20     int listsize;      /* 当前分配的存储容量(以sizeof(ElemType)为单位)*/
    21 } SqList, *SqListPtr;
    22 
    23 /* 初始化 */
    24 void init_sql(SqListPtr L)
    25 {
    26     printf("Please input the tatal of the team:");
    27     scanf("%d",&L->listsize);        /*输入敢死队员总数*/
    28 
    29     L->elem = (ElemType *)malloc((L->listsize+1) * sizeof(ElemType)); 
    30     if(! L->elem)
    31     {
    32         printf("分配内存失败\n");
    33         exit(0);  /*存储分配失败*/
    34     }
    35     else
    36     {
    37         L->length = L->listsize;
    38         int i;
    39         for(i = 1; i <= L->listsize; i++)
    40         {
    41             (L->elem)[i] = i;
    42         }
    43     }
    44 }
    45 
    46 /* 删除第pos位成员 */
    47 int delete_menber(SqListPtr L, int pos)
    48 {
    49     int i = pos;
    50     int d = L->elem[pos];
    51     for(; i <= L->length; i++)
    52     {
    53         L->elem[i] = L->elem[i+1];
    54     }
    55     L->length--;
    56     
    57     return d;
    58 }
    59 
    60 int performTask(SqListPtr L)
    61 {
    62     int i = 1, count = 1,d;
    63     for(i = 1; L->length > 1;)
    64     {
    65         if(count == 5)
    66         {
    67             count = 1;
    68             d = delete_menber(L,i);
    69             printf("第 %d 号去执行任务\n", d );
    70         }
    71         else
    72         {
    73             count++;
    74             i++;
    75         }
    76         if(i > L->length)
    77         {
    78             i = 1;
    79         }
    80     }
    81 }
    82 
    83 int main()
    84 {
    85     SqListPtr L = (SqListPtr)malloc(sizeof(SqList));
    86     init_sql(L);
    87     
    88     performTask(L);
    89     printf("最后剩下的人 %d 号\n",L->elem[1]);
    90     
    91     return 0;
    92 }

    方法二:

    解法二(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],程序也是异常简单:

     1 #include <stdio.h>
     2  int main()
     3  {
     4      int n, m, i, s = 0;
     5      printf ("N M = ");
     6      scanf("%d%d", &n, &m);
     7      for (i = 2; i <= n; i++)
     8      {
     9          s = (s + m) % i;
    10      }
    11      printf ("\nThe winner is %d\n", s+1);
    12  }
  • 相关阅读:
    【Azure 应用服务】由 Azure Functions runtime is unreachable 的错误消息推导出 ASYNC(异步)和 SYNC(同步)混用而引起ThreadPool耗尽问题
    【Azure API 管理】是否可以将Swagger 的API定义导入导Azure API Management中
    【Azure 应用服务】Azure Function 不能被触发
    【Azure 环境】Azure Key Vault (密钥保管库)中所保管的Keys, Secrets,Certificates是否可以实现数据粒度的权限控制呢?
    【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
    【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
    【Azure API 管理】Azure API Management通过请求中的Path来限定其被访问的频率(如1秒一次)
    【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
    【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
    【Azure 应用服务】App Service中抓取 Web Job 的 DUMP 办法
  • 原文地址:https://www.cnblogs.com/CocoonFan/p/3089977.html
Copyright © 2011-2022 走看看