zoukankan      html  css  js  c++  java
  • 约瑟夫问题

    全都是抄具体数学的

    问题(1)

    (n)个人围成一圈,顺时针从(1)(n)编号,并从(1)开始报数,如果一个人报的数字是(2)的倍数,就把他撒了,后面的人继续,问最终活下来的人的编号

    (Sol1)

    (J(n))为最终活下来人的编号,有(J(1)=1)

    如果有(2n)个人,那么第一圈过去之后所有偶数编号的人都被撒了,那么可以看成还有(n)个人,只不过第(i)个人的编号是(2i-1),所以有(J(2n)=2J(n)-1)

    如果有(2n+1)个人,那么第一圈过去之后所有偶数编号的人都被撒了,下一圈一开始(1)号被撒了,那么可以看成还有(n)个人,第(i)个人的编号是(2i+1),所以有(J(2n)=2J(n)+1)

    如果我们记(k)(n)的二进制最高位,有(J(n)=2(n-2^k)+1)打表证明即可,归纳证明即可

    问题(2)

    (n)个人围成一圈,顺时针从(1)(n)编号,并从(1)开始报数,如果一个人报的数字是(3)的倍数,就把他撒了,后面的人继续,问最终活下来的人的编号

    (Sol2)

    如果还是按上面的递归式的方法不用做了

    考虑一种新的方法,(1)号报完之后,我们给他一个新的编号(n+1)(2)号报完之后变成(n+2)(3)号报完就被撒了,(4)号报完变成(n+3)……同理,每一次令(3k+1)(3k+2)的人变成(n+2k+1)(n+2k+2)

    这样的话第(k)个死的人编号是(3k),最终活下来的人编号就是(3n)了,我们只要知道他原来的编号就行了

    对于编号为(p)的人,如果(p>n),那么(p=n+2k+v),其中(v=1)(2),所以有(k=lfloor{p-n-1over 2} floor),而它之前的号码是(3k+v),即(3k+(p-n-2k)=p+k-n),那么我们可以写出一个代码

    inline int calc(R int n){
    	R int p=3*n;
    	while(p>n)p=((p-n-1)>>1)+p-n;
    	return p;
    }
    

    关于复杂度的证明,它和下面这种方法等价

    如果我们记(d=3n+1-p),那么递归过程就变成了

    [egin{aligned} d &=3n+1-left(lfloor{{3n+1-d-n-1}over 2} floor+3n+1-d-n ight)\ &=n+d-lfloor{2n-dover 2} floor\ &=d-lfloor{-dover 2} floor\ &=d+lceil{dover 2} ceil\ &=lceil{3dover 2} ceil\ end{aligned} ]

    代码如下

    inline int calc(R int n){
    	R int d=1;
    	while(d<=(n<<1))d=ceil(1.5*d);
    	return 3*n+1-d;
    }
    

    由于这两种方法本质一样,所以复杂度都是(O(log n))

    问题(3)

    (n)个人围成一圈,顺时针从(1)(n)编号,并从(1)开始报数,如果一个人报的数字是(q)的倍数,就把他撒了,后面的人继续,问最终活下来的人的编号

    (Sol3)

    在问题(2)的基础上扩展一下即可

    inline int calc(R int n,R int q){
    	R int d=1;
    	while(d<=(q-1)*n)d=ceil(1.0*q/(q-1)*d);
    	return q*n+1-d;
    }
    

    参考资料

    《具体数学》

  • 相关阅读:
    show point on image
    vec2d
    Size类型如何调用和定义
    opencv数据类型和格式的坑
    IplImage 与mat之间的转换及释放内存
    linux平台程序高精度延时问题select-usleep等
    gettimeofday的使用
    列模式编辑文本
    【linux基础】如何开机自启动某个程序-linux工具-ubuntu
    查看camera设备-linux
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11961246.html
Copyright © 2011-2022 走看看