约瑟夫环的数学问题

 

        问题描述:有n个人围成一圈,分别编号为1,2,3,…,n-1,n,从1号开始报数,报到m的人出局,接着下一个人从1开始重新报数,再次报到m的人同样出局,如此循环,直到圈中只剩一个人为止,则此人为胜出者,求胜出者编号?

这是一个实际问题,可以用模拟报数过程方式来计算结果,但其时间复杂度过高,为O(m*n)。当然我们能够感觉到此问题应该可以用数学方式来解决,下面就让我们来看看如何用数学方式更加高效地解决此问题。

         由于计算需要,不失本质地改动一下问题描述。n个人的编号为0,1,…,n-1,每次报数从0开始,报到m-1出局。先看第一个出局的人,其编号为(m-1)%n,这个人出局之后我们再也不需要考虑他了。这时剩下n-1个人,构成一个新的环,从编号为(k=m%n)的人开始报数。即k,k+1,…,n-2,n-1,0,1,2,…,k-3,k-2,而k开始报0。

我们现在做一下编号对应变换:

(1)k     k+1   k+2   …     …      k-3    k-2(编号为k的人开始报0)

          |     |       |                              |       |

(2)0     1       2       …     …       n-3   n-2(编号为0的人开始报0)

(1)、(2)两式具有固定的对应关系,即如果(2)中编号为x的人最后胜出,那么就是(1)中编号为x~=(x+k)%n=(x+m%n)%n=(x+m)%n的人最后胜出。其实,式(2)就是由n-1人个人围成一圈的约瑟夫环,问题又回到原本,但规模却减一。如此看来,只要我们求出了问题规模为n-1的约瑟夫环,也就求出了问题规模为n的约瑟夫环。而n-1规模又可以由n-2规模求出,这就变成了一个递推问题。

递推公式:

设w(i)为i个人玩的的报到m出局的约瑟夫环的胜出者编号。

w(1)=0;

w(i)=(w(i-1)+m)%i; (i>1)

由于最终只需要w(n)的结果,而不需要保存中间结果,且如果编号是从1开始的,那么结果加一即可,故程序代码如下:

#include<iostream> 
using namespace std; 
int main()
{
    int n,m;  
    while(cin>>n&&n!=0){
        cin>>m;
        int w=0;//胜出者编号
        for(int i=2;i<=n;i++){ 
            w=(w+m)%i;
        }
        cout<<w+1<<endl;
    }
    system("pause"); 
    return 0;
}
其时间复杂度为O(n)。

样例输入:

1 10

8 5

6 6

0

样例输出:

1

3

4

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值