`
smruanjian
  • 浏览: 677 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

约瑟夫环问题两解

阅读更多
约瑟夫环问题(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等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

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

相关推荐

    关于“约瑟夫环“问题的另解

    关于 “约瑟夫环“ 问题的另解, 具体程序代码。

    PHP实现约瑟夫环问题的方法分析

    先来看看网上比较常见的约瑟夫环问题描述:约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1...

    C++约瑟夫环

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

    小白也能看懂的约瑟夫环问题

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

    约瑟夫环(python)

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

    约瑟夫环leetcode-LeetCodeAlgorithm:leetCode的算法

    约瑟夫环 leetcode leetCode 刷题经验 难题 序列号 题目名称 关键解法 4 Median of Two Sorted Arrays 要求O(logN) 5 实现 strStr() kmp算法 30 串联所有单词的子串 不懂 36 有效的数独 不懂 37 解数独 不懂 109 ...

    Josepfu.java

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

    C语言常见问题及算法专题整理资料

    约瑟夫环问题,魔方算法 迷宫探路 有向图强连通分量 线段树解在程序中的应用 Tarjan算法 Hash函数英语 RMQ算法

    数据结构与算法实验报告1

    一、 约瑟夫环问题仿真 二、 二叉排序树与平衡二叉树的实现 三、 迷宫问题 四、 哈夫曼压缩/解压缩算法

    极限环分支理论 英文 [HanMaoan] 2013年版

    为此,我们先要建立同宿环稳定性的判别量,然后较系统地研究同宿环、双同宿环与两点异宿环在扰动下产生极限环的个数问题。最后一章,即第五章,论述分支理论方法对平面一般多项式系统的一个有趣应用,基于某些3,4,5...

    数据结构实验经典题目

    约瑟夫环仿真; 教学计划编制问题 二叉排序树与平衡二叉树的实现 停车场模拟管理程序的设计与实现 学生成绩分析 一元稀疏多项式计算器 哈夫曼压缩/解压缩算法(编译码器) 全国交通咨询模拟系统

    常用算法代码

    | 约瑟夫环问题(数学方法) 27 | 约瑟夫环问题(数组模拟) 27 | 取石子游戏 1 27 | 集合划分问题 27 | 大数平方根(字符串数组表示) 28 | 大数取模的二进制方法 28 | 线性方程组 A[][]X[]=B[] 28 | 追赶法...

    C/C++常用算法手册.秦姣华(有详细书签).rar

    8.2.3 复杂约瑟夫环算法 247 8.2.4 复杂约瑟夫环求解 248 8.3 城市之间的最短总距离 250 8.3.1 最短总距离算法 250 8.3.2 最短总距离求解 253 8.4 最短路径 257 8.4.1 最短路径算法 258 8.4.2 最短路径求解 ...

    《妙趣横生的算法(C语言实现)》(杨峰 编著)

    7.4 约瑟夫环 7.5 二进制/八进制转换器 7.6 回文字符串的判定 7.7 括号匹配 7.8 魔王语言翻译 7.9 动态双向链表的应用 7.10 判断完全二叉树 7.11 动画模拟创建二叉树 7.12 打印符号三角形 7.13 递归函数的非递归...

    数据结构课程设计

    6、八皇后问题:设8皇后问题的解为 (x1, x2, x3, …,x8), 约束条件为:在8x8的棋盘上,其中任意两个xi 和xj不能位于棋盘的同行、同列及同对角线。要求用一位数组进行存储,输出所有可能的排列。 7、迷宫求解:用二维...

    ACM巨全模板 .pdf

    3.约瑟夫环 4.斐波那契博弈 (取的数依赖于对手刚才取的数) 5.sg函数 数论: 1.数论 素数检验:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验 2.拉格朗日乘子法(求有等式约束条件的极值) 3.裂项(多项式...

    java源码包实例源码JAVA开发源码55个合集.zip

    Java约瑟夫环演示Applet源码.rar java网络五子棋的源代码.rar JAVA网络抓包程序.rar Java转换xml.rar java项目源码在线相册系统.rar 书籍管理系统.rar 企业进销存管理系统.rar 传奇私服登录器Java版附源代码.rar ...

    C程序范例宝典(基础代码详解)

    实例092 约瑟夫环 122 实例093 创建顺序表并插入元素 123 实例094 向链表中插入结点 125 实例095 从链表中删除结点 126 实例096 合并两个链表 129 实例097 单链表就地逆置 130 实例098 头插入法建立...

    抽象代数 [王颖,南基洙 编著] 2013年版

    抽象代数 出版时间:2013年版 丛编项: 高等学校教材 内容简介  《高等学校教材:抽象代数》介绍了抽象代数学中最基本的内容,共4章。...第8节 根式扩张与解方程 第9节 尺规作图 附录 参考文献 名词索引 符号索引

    《近世代数引论(第3版)》作者: 冯克勤、李尚志、章璞 出版年: 2009年

    附录2.1 高斯整数环与二平方和问题 2.5 多项式环 2.6 域的扩张 附录2.2 对称多项式 附录 2.3 代数基本定理的一个证明 附录2.4 可以三等分角吗 2.7 有限域 第3章 域的伽罗瓦理论 3.1 域的扩张(复习),分裂域 3.2 ...

Global site tag (gtag.js) - Google Analytics