• 约瑟夫环问题


    问题描述:有n个人,编号分别从0到n-1排列,这n个人围成一圈,现在从编号为0的人开始报数,当报到数字m的人,离开圈子,然后接着下一个人从0开始报数,依次类推,问最后只剩下一个人时,编号是多少?

    分析:这就是著名的约瑟夫环问题,关于来历不再说明,这里直接分析解法。

            解法一:蛮力法。我曾将在大一学c语言的时候,用蛮力法实现过,就是采用标记变量的方法即可。

            解法一:循环链表法。从问题的本质入手,既然是围成一个圈,并且要删除节点,显然符合循环链表的数据结构,因此可以采用循环链表实现。

            解法三:递推法。这是一种创新的解法,采用数学建模的方法去做。具体如下:

                       首先定义一个关于n和m的方程f(n,m),表示每次在n个编号0,1,...,n-1中每次删除的报数为m后剩下的数字,

                       在这n个数字中,第一个被删除的数字是(m-1)%n,为了简单,把(m-1)%n记作k,那么删除k之后剩下的数字为0,1,2,...,k-1,k+1,...,n-1

                       并且下一次删除的数字从k+1开始计数,这就相当于剩下的序列中k+1排在最前面,进而形成k+1,..,n-1,0,1,2,...,k-1这样的序列,这个序列最后剩下的数                      字应该和原序列相同,由于我们改变了次序,不能简单的记作f(n-1,m),我们可以记作g(n-1,m),那么就会有f(n,m)=g(n-1,m).

                      下一步,我们把这n-2个数字的序列k+1,..,n-1,0,1,2,...,k-1做一个映射,映射的结果是形成一个从0到n-2的序列。

                               k+1对0,k+2对1,......,n-1对n-k-2,0对n-k-1,1对n-k,....,k-1对n-2

                     这样我们可以把这个映射定义为p,则p(x)=(x-k-1)%n,它表示如果映射前的数字是x,映射后为(x-k-1)%n,从而这个映射的反映射问为p-1(x)=(x+k+1)%n

                     由于映射之后的序列和原始序列具有相同的形式,都是从0开始的序列,所以可以用函数f来表示,即为f(n-1,m),根据映射规则有:

                     g(n-1,m)=p-1[f(n-n,m)]=[f(n-1,m)+k+1]%n,最后把之前的k=(m-1)%n带入式子就会有f(n,m)=g(n-1,m)=[f(n-1,m)+m]%n.

                    这样我们就可以得出一个递推公式,

                                                                当n=1时,f(n,m)=0;

                                                                当n>1时,f(n,m)=[f(n-1,m)+m]%n;

                    有了这个公式,问题就变得多了。

          由于解法一和解法二,都很好理解,我就不再写具体的代码了,这里我给出解法三Java代码,用递归实现:

     1 import java.util.*;
     2 public class Main{
     3     public static int ysfh(int n,int m){
     4         if(n<=1)return 0;
     5         return (ysfh(n-n,m)+m)%n;
     6     }
     7     public static void main(String[] args) {
     8         // TODO 自动生成的方法存根
     9         Scanner scan=new Scanner(System.in);
    10         int n=scan.nextInt();
    11         int m=scan.nextInt();
    12         System.out.println("最后剩下的编号为:"+ysfh(n,m));
    13     }
    14 
    15 }

    测试样例输出为:

    10   3
    最后剩下的编号为:3

    可以看出来,这么复杂的一个问题递归只需要两行代码即可实现,因此在今后的编程的过程中,要灵活运用数学知识。

                       

  • 相关阅读:
    nginx 优化
    linux 内核的优化
    Linux下如何查看版本
    oracle安装数据库中文乱码解决办法
    Python 5 行代码的神奇操作
    Python爬取网站上面的数据很简单,但是如何爬取APP上面的数据呢
    解放双手!用 Python 控制你的鼠标和键盘
    js混淆、eval解密
    ubuntu
    爬虫基本原理
  • 原文地址:https://www.cnblogs.com/guozhenqiang/p/5489362.html
Copyright © 2020-2023  润新知