约瑟夫斯问题及其编程

来源:互联网 发布:java 驻场开发 编辑:程序博客网 时间:2024/06/02 06:23

约瑟夫斯(Josephus)问题是一个出现在计算机科学数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。并且约瑟夫斯问题的描述是这样的:有n个囚犯站成一个圆圈,准备处决。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

    然后问题是,给定了n和k,一开始要站在什么地方才能避免被处决?

    在约瑟夫斯问题里详细介绍了其中k=2时此问题的数学方式的解法,并得到了一个可以通过数学归纳法证明的定理:

如果n=2^m+l0\leq l<2^m,则f(n) = 2l+1

    比较令人眼前一亮的是这个结论的表现形式竟与整数n的二进制表示有关:即把n的第一位移动到最后,便得到f(n)。如果n的二进制表示为n=b_0 b_1 b_2 b_3\dots b_m,则f(n)=b_1 b_2 b_3 \dots b_m b_0。这点很容易证明:因n的第一位是1,并且其余位的十进制值就是l,然后左移一位(即2*l)再加上第一位的值就变成了f(n)的值了。

    接着使用动态规划的方法得到一般情况下(即k\neq 2)的递推公式:

当n=1时,f(1,k)=1

但n>1时,f(n,k)=(f(n-1,k)+k) \bmod n

此时先开始用数学方法来实现约瑟夫斯问题的编程,其代码如下(使用递推方式):

#include <iostream>using namespace std;int Josephus(int n, int k);int main(){    int n, k, ret;    cout << "Please Enter Two number(n, k): ";    cin >> n >> k;        ret = Josephus(n, k);        cout << "The final winner is " << ret << endl;        system("pause");    return 0;    }        int Josephus(int n, int k)    {        int ret;        if(1 == n)             return ret = 1;        else        {            ret = (Josephus(n-1, k) + k)%n;            if(0 == ret)                 ret = n;            return ret;            }        }

当输入n=9、k=5时最后一个数是8,下图就是执行的结果:


注意:这里必须要对Josephus(n, k)的值进行判断,因Josephus(n-1, k)+k>=k>0,所以当Josephus(n, k)为0时,必须要将其赋值为n。(为什么是n而不是n*m?其中m是大于1的任意正整数)

例如注释掉上述代码中的这段代码:if(0 == ret) ret = n;

执行的结果如下所示:


此时结果是错误的,最后留下来的人应该是9才对。

同时这里的递推可以用循环来实现:

#include <iostream>using namespace std;int main(){    int n, k, ret = 1, i;    cout << "Please Enter Two Number(n, k): ";    cin >> n >> k;        for(i = 2; i <= n; i++)    {          ret = (ret + k)%i;          if(0 == ret)               ret = i;          }              cout << "The final winner is " << ret << endl;        system("pause");    return 0;    }

执行结果如下图所示:


接下来使用链表方法来实现:

这里解决这个问题的核心步骤(即程序的基本算法)是:
1)建立一个具有n个链接点而无头结点的循环链表;
2)确定第一个报数人的位置即m;
3)不断地从该链表中删除报数人处的链接点,直到该链表为空。
其代码如下:
#include <iostream>using namespace std;typedef struct node{        int data;        struct node *next;        }Node;        void createList(Node* &head, Node* &tail, int n);void print(Node* &head);void countPrint(Node* &head, Node* &tail, int k);int main(){    Node *head, *tail;    int n, k;    cout << "Please Enter Two numbers(n, k): ";    cin >> n >> k;        createList(head, tail, n);    print(head);    countPrint(head, tail, k);        system("pause");    return 0;    }    void createList(Node* &head, Node* &tail, int n){     if(n < 1)     {          head = NULL;          return;          }     head = new Node;     head->data = 1;     head->next = NULL;          Node *p = head;     for(int i = 2; i < n+1; i++)     {             p->next = new Node;             p = p->next;             p->data = i;             p->next = NULL;             }          tail = p;     p->next = head;     }     void print(Node* &head){     Node *p = head;          do     {             cout << " " << p->data << " ";             p = p->next;             }while(p != head);     cout << "\n";     }     void countPrint(Node* &head, Node* &tail, int k){     Node *cur = head;     Node *pre = tail;          int cnt = k;     while(cur && cur != cur->next)     {               if(--cnt)               {                        pre = cur;                        cur = cur->next;                        }               else               {                   pre->next = cur->next;                   cout << " " << cur->data << " ";                   delete cur;                   cur = pre->next;                   cnt = k;                   }               }     if(cur == cur->next)     {                   cout << " " << cur->data << endl;                   delete cur;                   head = tail = NULL;                   }     }
执行结果如下图所示:

参考资料:http://blog.csdn.net/zhuimengzh/article/details/6727221


原创粉丝点击