递推公式法解决约瑟夫问题

约瑟夫问题的描述: n个人围成一圈,第一个人从1开始报数,报m的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的幸存者的编号(编号从零开始)。

如果需要知道每一轮被杀掉的人的编号的话,最好的方法可能是使用链表。不过如果只需要知道最后幸存者的编号的话,链表法的时间复杂度是无法接受的,使用递推公式解决这题会快很多。

递推公式百度一下你就知道,但关键是这个递推公式是如何推导出来的呢?

我们的目的当然是希望能用m和n表示出最后的幸存者的编号,但不幸的是这很难直接办到。但是用m和n表示出第一个被杀掉的人的编号并不难。那么如何表示呢?请大家先思考一下,忍住不要往下看~

↓从此处开始一光年↓

如果你的答案是(m - 1) mod n的话,那么你是对的!这个答案应该不难理解。

(注:mod表示取余)

现在我们已经杀掉一个人了,那么就还剩n - 1个人。因为我们是在找递推关系,所以如果能找到n个人的情况与n - 1个人的情况之间的关系的话,递推公式就出来了。而现在刚好圆圈里剩下n - 1个人,就意味着我们接下来需要重点分析这剩下的n - 1个人。

根据第一个被杀掉的人的编号,我们可以用m和n表示出其他人的编号,如下图。

接下来让我们对每个人重新编号。我们把第二轮报数中第一个报数的人,也就是第一个被杀死的人后面的那个人,重新编号为0,后面的人依次为1,2,3....。

哈哈。接下来仔细一看,问题不就已经被转换成了: n - 1个人围成一圈,第一个人从1开始报数,报m的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的幸存者的编号(编号从零开始)。除了“n个人”变成了“n - 1个人"以外,其他条件完全没变。

另外,我们也不难观察出新编号和旧编号之间的关系。容易观察出,如果一个人的新编号是i的话,那么他的旧编号就是(m + i) mod n。

到了现在,我们已经可以很轻松地推导出递推公式了。我们定义这样一个函数f(x,y) : f(x,y)表示当约瑟夫问题中的n = x,m = y时幸存者的编号。回到上面n - 1个人的情况,显然,幸存者的新编号就是f(n - 1,m),那么根据我们刚刚发现的新编号与旧编号之间的关系,就可以得出幸存者的旧编号是(m + f(n - 1,m)) mod n,这个不正好就是我们所寻找的递推关系嘛?

就讲到这里吧。至于具体如何用这个递推公式解决约瑟夫问题,太简单了,就留给你们自己想a.a

原文地址:https://www.cnblogs.com/ZhouYiJoe/p/12147052.html

时间: 2024-10-15 23:28:35

递推公式法解决约瑟夫问题的相关文章

循环链表解决约瑟夫问题

训练一下尾插法和循环链表的使用. //循环链表解决约瑟夫问题 #include <stdio.h> #include <stdlib.h> typedef struct CycleLinkList { int data; struct CycleLinkList * next; }cycleLinkList; cycleLinkList * h, * r, * s; // 头指针.尾指针 int main() { int i, j; int n, num; cycleLinkLis

Java 解决约瑟夫问题

约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环.又称“丢手绢问题”.) 有这样一个故事,15个教徒和15个非教徒在深海遇险必须讲一半的人投到海中,其余的人才能获救,于是想出这样过一个办法,30个人围城一圈.从第一个人开始一次报数,每次数到9,就将这个人扔到海中,直到剩余15个人为止.问怎样的排法,使得每次扔到海中都是非教徒 现在的一种办法就是一个Boolean数组来模拟30个人,非教徒为false.刚开始都没true,当数到

C++ 用循环链表解决约瑟夫环问题

约瑟夫环问题 已知 n 个人(n>=1)围坐一圆桌周围,从 1 开始顺序编号,从序号为 1 的人开始报数,顺时针数到 m 的那个人出列.下一个人又从 1 开始报数,数到m 的那个人又出列.依此规则重复下去,直到所有人全部出列.请问最后一个出列的人的初始编号. 要求 输入人数 n,所报数 m,输出最后一个人的初始编号. 解决思路 首先因为是圆桌问题,使用链表解决的话需要构建循环链表. 接着是出列问题,这里我的设计思路是将指向链表的指针移动到需要出列的人的位置,然后根据正常的链表删除进行操作即可.

算法学习 - HashTable开放地址法解决哈希冲突

开放地址法解决哈希冲突 线性开放地址法 线性开放地址法就是在hash之后,当发现在位置上已经存在了一个变量之后,放到它下一个位置,假如下一个位置也冲突,则继续向下,依次类推,直到找到没有变量的位置,放进去. 平方开放地址法 平方地址法就是在hash之后,当正确位置上存在冲突,不放到挨着的下一个位置,而是放到第2^0位置,假如继续冲突放到2^1的位置,依次2^3... 直到遇到不冲突的位置放进去. 双散列开放地址法 双散列同上,不过不是放到2^的位置,而是放到key - hash(key, tab

算法学习 - Hash Table操作,分离链接法解决哈希冲突

分离链接法 hash table是映射机制的,最大的优点就是它的操作是O(1)级别的.但是会出现哈希冲突,这就需要几种办法来解决.这里先说一种:分离链接法. 就是当插入的位置已经存在一个值之后,那么在这个值之后插入,就可以了,也叫拉链法.(但是其实会降低查找速度,变成O(n)级别) 下面是代码: // // main.cpp // HashTable_SeparateChaining // // Created by Alps on 14-8-5. // Copyright (c) 2014年

拉链法解决Hash节点冲突问题

<?php /* * hash::拉链法解决hash节点存储冲突问题 * ::2014-07-02 * ::Small_Kind */ class small_hash { private $size = 20;//hash节点大小 private $zone = null;//hash空间 //实例化函数,并设置一个初始hash节点大小,如果节点大小为null,则为默认节点大小 final public function __construct($size = null){ if(!is_nu

C++循环链表解决约瑟夫环问题

约瑟夫环问题可以简单的使用数组的方式实现,但是现在我使用循环链表的方法来实现,因为上午看到一道面试题规定使用循环链表解决约瑟夫环问题. 什么是约瑟夫环? “约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列.”(百度百科中的解决办法列出了很多,可以看到循环链表并不是最简单的方法) 这道面试题考察了循环链表的“创建”,

C语言用数组解决约瑟夫环问题

       在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,大家决定宁愿自杀也不要被敌人抓到,于是确定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止.然而约瑟夫和他的朋友并不想死去,那么他应该怎样安排他和他的朋友的位置,才能逃脱这场死亡游戏呢?         用C语言解决约瑟夫环问题的最佳方式是采用循环链表,但使用数组同样也可以解决瑟夫环的问题.采用循环链表的方法,以后详述.本节主

php解决约瑟夫环的问题

php里面解决约瑟夫环还是比较方面的,但是下面的方法太费空间 <?php class SelectKing{ private $m;//幅度 private $n;//总数 public function __construct($m,$n){ $this->m = $m; $this->n = $n; } public function getKing(){ $mokeys = range(1, $this->n); $tmp = 0; while(count($mokeys)&