约瑟夫问题

Posted yuanquming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了约瑟夫问题相关的知识,希望对你有一定的参考价值。

全都是抄具体数学的

问题(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;
}

参考资料

《具体数学》

以上是关于约瑟夫问题的主要内容,如果未能解决你的问题,请参考以下文章

约瑟夫问题Java环形单向链表代码实现

环形链表与约瑟夫问题的代码解决

约瑟夫问题的JAVA实现(借鉴别人的代码+自己分析理解)

看小朋友做游戏顿时解决了约瑟夫问题

java中约瑟夫环代码实现

年前最后一波装逼记一次阿里面试,我是如何用一行代码解决约瑟夫环问题的