一. 问题描述

  已知n个人,分别以编号1,2,3,...,n表示,围坐在一张圆桌周围。从编号为k的人开始报数1,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列,求最后一个出列人的编号,可记为P(n,m,k),或记为P(n,m,k,s = 1),其中s为起始编号。

二. 递归求解

  n(假设n值很大,而k、m值都很小)个人围成一圈,从k开始以m为步长报数,第k+m-1个人出列;于是转化为n-1个人围成一圈,从(k+m-1)+1开始以m为步长报数,第(k+m)+m-1个人出列;再转化为求n-2个人围成一圈,从(k+2m-1)+1开始以m为步长报数,第(k+2m)+m-1个人出列;依次类推,直至只剩1个人围成一圈,该人出列即为问题的解。有如下推导过程:

  1. 因为第一个出圈者是k+m-1,则下一个起始报数者k+m,原圈可增加一套新编号a,用1表示k+m,2表示k+m+1,依此类推,n则表示k+m-1,于是原圈又可对应至新环:1,2,......,n-1,n ------ ①。
  2. 因为k+m-1将出圈,即①中n将出圈,余下n-1人,于是有:1,2,......,n-1 ------ ②,对②求P(n-1,m,1),则P(n > 1,m,k) = (P(n-1,m,1) + (k + m - 2)) % n + 1 ------ ⑴(取模前先减1,取模后再加1,以保证落在[1,n]这个区间之上)。由于形如(k + num) % n = (k % n + num) % n (num >= 0)是成立的,所以当k>n或m>n时,⑴也是成立的。
  3. 当k=1,有P(n > 1,m,1) = (P(n-1,m,1) + m - 1) % n + 1 ------ ⑵,下文的Josephus_by_k_eq_1(n, m, p = 1, i = 1)实现了⑵。
  4. 一般地,P(1,m,k) = P(1,1,1) = 1 ------ ⑶。
  5. 由⑵可知P(n-1,m,1) = (P(n-2,m,1) + m - 1) % (n-1) + 1 ------ ⑷,于是P(n,m,k) 的求解可分作两步完成,下文的Josephus_by_k(n, m, k)实现了这一思想:
    步骤一:通过等式⑶、⑷递推求解P(n-1,m,1)
    
    步骤二:通过等式⑴求解P(n,m,k)
  6. 当约瑟夫环不是用1~n进行编号时,而是用1 + (s-1)~n + (s-1)进行任意编号,最后出圈者P(n,m,k,s) = P(n,m,k-(s-1)+ (s-1) ------ ⑸,下文的Josephus_by_k_and_s(n, m, k, s)实现了 ⑸。

 

  需特别指出的是,上述推导过程并没有直接得到P(n,m,k > 1)的某种递推关系,只是解决了P(n,m,k = 1)这个特列的递推关系。对于⑵有人给出如下的类似描述,实在费解。因为其描述中k最终实际是一个无关的参数,但P(n,m,k)却与k有极大的关联。事实上,P(3,2,3) = 2,而P(3,2,1) = 3 。

P(n, m, k) = 1  (i = 1)

P(n, m, k) = (P(i - 1, m, k ) + m - 1) % n + 1  (i > 1)

其下列算法实现虽然在注释中给k定义为起始报数位置,实际上不是P(n, m, k)中k的含义,而是代表P(n-1, m, 1)
long Josephus(long n,long m,long k){ //参数分别为:总人数,出圈步长,起始报数位置,
for (long i = 1; i <= n; i++)
k = (k + m - 1) % i + 1;
return k; //返回最后一人的位置
}

三. P(n,m,1) = (P(n-1,m,1) + m - 1) % n + 1的Javascript算法实现:Josephus_by_k_eq_1(n, m, p = 1, i = 1)

/**
* 约瑟夫环(编号1~n)问题求最后出圈者P(n,m,k = 1)
*
* @para int n 总人数
* @para int m 报数步长
* @para int p P(n-1,m,1)
* @para int i 迭代控制变量
* @return int 最后出圈者
*/ function Josephus_by_k_eq_1(n, m, p = 1, i = 1) {
if (i > n) {
return p; //返回最后出圈者
} else {
p = (p + m - 1) % i + 1;
i = i + 1; return Josephus_by_k_eq_1(n, m, p, i);
}
}

四. P(n,m,k) = (P(n-1,m,1) + (k + m - 2)) % n + 1Javascript算法实现:Josephus_by_k(n, m, k)

/**
* 约瑟夫环(编号1~n)问题求最后出圈者P(n,m,k)
*
* @para int n 总人数
* @para int m 报数步长
* @para int k 起始报数者
* @return int 最后出圈者
*/ function Josephus_by_k(n, m, k) {
return (Josephus_by_k_eq_1(n - 1, m) + (k + m - 2)) % n + 1;
}

五.P(n,m,k,s) = P(n,m,k-(s-1)+ (s-1)Javascript算法实现:Josephus_by_k_and_s(n, m, k, s)

/**
* 约瑟夫环(编号1+(s-1)~n+(s-1))问题求最后出圈者P(n,m,k,s)
*
* @para int n 总人数
* @para int m 报数步长
* @para int k 起始报数者
* @para int s 起始编号
* @return int 最后出圈者
*/ function Josephus_by_k_and_s(n, m, k, s) {
return Josephus_by_k(n, m, k - (s - 1)) + (s - 1);
}

关于递推算法求解约瑟夫环问题P(n,m,k,s)的更多相关文章

  1. 基本算法思想之递推算法思想(C++语言描述)

    递推算法是非常常用的算法思想,在数学计算等场合有着广泛的应用.递推算法适合有明显公式规律的场合. 递推算法基本思想 递推算法是一种理性思维莫斯的代表,根据已有的数据和关系,逐步推到而得到结果.递推算法 ...

  2. 【约瑟夫环变形】UVa 1394 - And Then There Was One

    首先看到这题脑子里立刻跳出链表..后来继续看如家的分析说,链表法时间复杂度为O(n*k),肯定会TLE,自己才意识到果然自个儿又头脑简单了 T^T. 看如家的分析没怎么看懂,后来发现这篇自己理解起来更 ...

  3. 组合数学--约瑟夫环问题 Josephus

    约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环. 有n个囚犯站成一个圆圈,准备处决.首先从一个人开始,越过k-2个人(因为第 ...

  4. 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)

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

  5. 通过例子进阶学习C++(六)你真的能写出约瑟夫环么

    本文是通过例子学习C++的第六篇,通过这个例子可以快速入门c++相关的语法. 1.问题描述 n 个人围坐在一个圆桌周围,现在从第 s 个人开始报数,数到第 m 个人,让他出局:然后从出局的下一个人重新 ...

  6. C++版 - 剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题,ZOJ 1088:System Overload类似)题解

    剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题) 原书题目:0, 1, - , n-1 这n个数字排成一个圈圈,从数字0开始每次从圆圏里删除第m个数字.求出这个圈圈里剩下的最后一个数字 ...

  7. Joseph POJ - 1012 约瑟夫环递推

    题意:约瑟夫环  初始前k个人后k个人  问m等于多少的时候 后k个先出去 题解:因为前k个位置是不动的,所以只要考虑每次递推后的位置在不在前面k个就行 有递推式 ans[i]=(ans[i-1]+m ...

  8. LA 3882 经典约瑟夫环问题的数学递推解法

    就是经典约瑟夫环问题的裸题 我一开始一直没理解这个递推是怎么来的,后来终于理解了 假设问题是从n个人编号分别为0...n-1,取第k个, 则第k个人编号为k-1的淘汰,剩下的编号为  0,1,2,3. ...

  9. 51nod 1073约瑟夫环 递归公式法

    约瑟夫环问题的原来描述为,设有编号为1,2,--,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,- ...

随机推荐

  1. Reversing-x64Elf-100

    一道很简单的小题 作为python小白这道题主要是学习了一点python知识...... 可以看出来 sub_4006FD 这个函数是用来判断输入密码是否正确的 我们看一下它的伪代码: signed ...

  2. 学习 Spring (九) 注解之 @Required, @Autowired, @Qualifier

    Spring入门篇 学习笔记 @Required @Required 注解适用于 bean 属性的 setter 方法 这个注解仅仅表示,受影响的 bean 属性必须在配置时被填充,通过在 bean ...

  3. Web API 配置Help Page

    当你创建一个web API,它通常用于创建一个帮助页面,以便其他开发人员知道如何调用你的API.你可以手动创建所有的文档,但最好是autogenerate尽可能多. 简化这个任务,ASP.Web AP ...

  4. APP需求调研、对比

    二.人脸验证 1.芝麻认证 : 0.4元/次,需要企业企业认证.不能有与芝麻信用类似的业务,如:保险... 2.旷视 : 0.5/次.企业认证.业务限制 3. 百度人脸识别 :  企业认证. 4.科大 ...

  5. linux 目录分类与文件操作

    / 虚拟根目录 一般不会在这里存储文件 /bin 二进制目录,存放需要GNU用户级的工具 /boot 启动目录,存放启动文件 /dev 设备目录,linux在这里创建设备节点 /etc 系统配置文件目 ...

  6. 实验吧 WEB 貌似有点难

    错误!你的IP不在允许列表之内! 提示:代码审计 这个提示可谓是非常良心了,一看源代码是一个识别ip地址的东西,如果IP为1.1.1.1那么就会得到KEY. 第一个if是判断是否有client-ip ...

  7. Android 模块化/热修复/插件化 框架选用

    概念汇总 动态加载:在程序运行的时候,加载一些程序自身原本不存在的文件并运行这些文件里的代码逻辑.动态加载是热修复与插件化实现的基础. 热修复:修改部分代码,不用重新发包,在用户不知情的情况下,给ap ...

  8. MySQL字段属性NUll的注意点

    MySQL字段属性应该尽量设置为NOT NULL 除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL.这看起来好像有点争议,请往下看. 空值("&quo ...

  9. Quartz基础+实例

    1. 介绍 Quartz体系结构: 明白Quartz怎么用,首先要了解Scheduler(调度器).Job(任务)和Trigger(触发器)这3个核心的概念. 1. Job: 是一个接口,只定义一个方 ...

  10. 用贝叶斯定理解决三门问题并用Python进行模拟(Bayes' Rule Monty Hall Problem Simulation Python)

    三门问题(Monty Hall problem)也称为蒙提霍尔问题或蒙提霍尔悖论,出自美国的电视游戏节目<Let’s Make a Deal>.问题名字来自该节目的主持人蒙提·霍尔(Mon ...