Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1066574
  • 博文数量: 77
  • 博客积分: 11498
  • 博客等级: 上将
  • 技术积分: 1840
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-04 11:10
文章分类

全部博文(77)

文章存档

2011年(1)

2010年(16)

2009年(5)

2008年(55)

分类: C/C++

2008-10-19 21:45:33


    约瑟夫环问题注记
    作者:tyc611.cublog.cn,2008-10-19
约瑟夫环问题J(n,m):有编号为1,2,3,...,n的n个人按顺时针站成一圈。然后,从编号为1的人开始沿顺时针方向从1开始报数,数到m的人出列,下一个人重新从1开始报数。重复此过程,直到最后剩下一人。请问最后剩下一人的编号J(n,m)是多少。

为了方便分析问题,我们把n个人的编号调整为0,1,2,...,n-1,此时的结果记为JJ(n,m),则J(n,m) = JJ(n,m)+1。

对于编号为0,1,2,...,n-1的n个人,第一个出列的人的编号为(m%n - 1)。剩下的(n-1)个人的编号为0,1,...,k-2,k,...,n-1(其中,k=m%n),重新排列为P1=k,k+1,...,n-1,0,1,...,k-2。

可利用公式(i-k)%n(其中,i为前面排列中的每一个编号)把排列P1转换为排列P2=0,1,...,n-1-k,n-k,n-k+1,...,n-2,从而得到子问题JJ(n-1,m)。也可以通过公式(i+k)%n把排列P2转换回为排列P1。

因此,原问题的解为JJ(n,m) = (JJ(n-1,m)+k)%n = (JJ(n-1,m)+m%n)%n = (JJ(n-1,m)+m)%n。

于是,我们得到递推公式
    JJ(n,m) = (JJ(n-1,m)+m)%n
    JJ(n-1,m) = (JJ(n-2,m)+m)%(n-1)
    ...
    JJ(2,m) = (JJ(1,m)+m)%2

而JJ(1,m) = 0,由前面的递推公式从JJ(1,m)可推算得到JJ(n,m)。进一步得到J(n,m) = JJ(n,m)+1。

实现代码如下:

#include
using namespace std;

int main()
{
    int n, m;
   
    while (true) {
        cout << "Input 'n' and 'm': \n";
        cin >> n >> m;
        if (cin.eof() || cin.fail())
            break;
       
        int x = 0;
        for (int i = 2; i <= n; ++i)
            x = (x + m) % i;
        cout << "The last one: " << (x + 1) << '\n' << endl;
    }
}

运行测试结果:
Input 'n' and 'm':
10 4
The last one: 5

Input 'n' and 'm':
578921563 2
The last one: 84101303

Input 'n' and 'm':
^D

对于一般约瑟夫环问题J(n,m),上面给出的是一个很好的解答。但对于m=2的特殊情形J(n,2),有一个特殊的解法(详细分析见《Concrete Mathematics》(2nd)节1.3,这里只给出结论)。

如果把n表示二进制数 n = (b[k]b[k-1]...b[1]b[0])B,那么
J(n,2) = J((b[k]b[k-1]...b[1]b[0])B) = (b[k-1]...b[1]b[0]b[k])B
也就是,J(n,2)等于把n循环左移1位。

实现代码如下:

#include
using namespace std;

template
int GetValidBits(UnsignedInteger number)
{
    int count = 0;
    int bits = sizeof(UnsignedInteger) * 8 / 2;
    while (number > 0) {
        if (bits == 0) {
            count += 1;
            break;
        }
        if (number >> bits) {
            count += bits;
            number >>= bits;
        }
        bits /= 2;
    }
    return count;
}

int main()
{
    while (true) {
        unsigned int n;
        cout << "Input 'n' for J(n,2): \n";
        cin >> n;
        if (cin.eof() || cin.fail())
            break;
        if (n == 0)
            continue;
       
        int bits = GetValidBits(n);
        unsigned int mask = 1;
        mask <<= bits - 1;
        unsigned int x = ((n & ~mask) << 1) | 1;
        cout << "The last one: " << x << '\n' << endl;
    }
}

程序运行测试结果如下:
Input 'n' for J(n,2):
10
The last one: 5

Input 'n' for J(n,2):
100
The last one: 73

Input 'n' for J(n,2):
578921563
The last one: 84101303

Input 'n' for J(n,2):
^D

显然,第二个程序的效率要高很多,特别是输入的数很大时!


阅读(1629) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~