約瑟夫問題(Josephus問題)的遞推O(n)解法、迴圈解法、單迴圈鏈表解法

來源:互聯網
上載者:User

(一) O(n)時間的解法

           只能解出最後一個出局的人,也就是最後留下的人——預設是從編號為1的人開始數起

 

n個人在一個圓桌上吃飯,每m個人殺掉一個,直到最後剩下一個人。
問最後剩下哪個人?

將人分別標記為0,1,2,...,n-1,得到n的一個完全剩餘系。
可得如下遞推公式:
            /- (J(n-1,m)+m+1) mod n, 當n>1
J(n,m)=|                                                , n,m∈Z
            /- 0, 當n=1

今天資料結構課在講這個東西的鏈表類比解法。我就開始想數論解法。
這個問題以前困惑過我很久,今天終於想到了用李昌勇教的遞迴方法來解。

下附網上搜到的一個證明:

這就是Josephus問題
設n個人圍成一圈,標號為0..n-1,從第一個人開始依次從1到k迴圈報數,當報到k的時候此人出圈。設J(n, k, i)表示第i個出圈的人的標號。

定理一:
J(n, k, 1) = (k-1) mod n, (n >= 1, k >= 1) ………… (1)

證明:
由定義直接得證。Q.E.D.

定理二:
J(n+1,k, i+1) = (k + J(n, k, i)) mod (n+1), (n >= 1, k >= 1, 1<= i <= n) ………… (2)

證明:
設J(n, k, i) = g,因此如果有n個人,從0開始報號,第i個出圈的標號為g。現在考慮J(n+1, k,i+1),因為J(n+1, k, 1) = (k-1) mod (n+1),即第一步的時候刪除數字(k-1) mod (n+1),第二步的時候從數字k開始數起。因而問題變為了找到剩下的n個數字中從k開始數起被刪除的第i個數字(注意這時(k-1) mod (n+1)已經被刪除了),而這恰好就是(g+k) mod (n+1),(2)成立。 Q.E.D.

根據(2),很容易求得n個數裡面第i個出圈的數。

對於k = 2, 3的情況,直接可以推匯出公式來。但是對於k>=4的情況,還沒有推匯出公式來,目前最好的演算法是一個根據估計J(n, k, i)上下界的快速演算法。

更具體的分析,參見
[1] Lorenz Halbeisen, The Josephus Problem, 1994
[2] Woodhouse, D. , The extended Josephus problem, Rev.Mat. Hisp.-Amer.(4) 33 (1973), 207-218
[3] Robinson, W. J.,The Josephus problem, Math. Gazette 44 (1960), 47-52
[4] Jakobczyk, F. , On the generalized Josephus problem, Glasgow Math.J.14(1973), 168-173

以及一個程式:
//函數接收n和m(n個人在一個圓桌上吃飯,每m個人殺掉一個),返回最後出圈的是第幾個人
/*e.g. yuesefu(5,2)=3
yuesefu(2,100)=1*/

 

int yuesefu(int n,int m)   //預設是從編號為1的人開始數起
{
int i,r=0;
for (i=2;i<=n;i++) r=(r+m)%i;
return r+1;
}

 

總結:這個演算法的時間複雜度為O(n),相對於類比演算法已經有了很大的提高。算n,m等於一百萬,
一千萬的情況不是問題了。可見,適當地運用數學策略,不僅可以讓編程變得簡單,而且往
往會成倍地提高演算法執行效率。  

 

或參見:http://blog.csdn.net/lvroyce/archive/2009/02/13/3883760.aspx

 

(二) 一般解法

 

#include <iostream>
using namespace std;
//n是人數(編號1,2,……,x),m是出列號,k是起始人編號
void Josefus(int n,int m,int k,int a[])
{
 int j=0, l=0;
 while (l<=n)
    {
        for (int i=1;i<=n;i++)
        {
            if (a[i]==1)
            {
                j++;
                if (j==m)
                {//滿足出列號
                    a[i]=0;
                    if (i==n&&k>1)
                    {
                        cout<<1<<endl;
                    }
                    else
                    {
                        cout<<i+(k-1)<<endl;
                    }
                    j=0;
                    l++;
                }
            }
        }
    } 
}

int main()
{
 int n,m,k;
 cout<<"請輸入參數: n--總人數,m--每m個人出局,k--起始人編號: ";
 cin>>n>>m>>k;
 int *a = new int[n];
 for(int i=1;i<=n;i++)
  a[i] = 1;
 Josefus(n,m,k,a);
 delete []a;
 return 0;
}

(三)鏈表解法

 

#include <iostream.h>
#include <stdlib.h>
typedef struct Node
{
 int data;
 struct Node* next;
}LNode, *LinkList;

//n為總人數,m為出列者喊到的數,k為第一個開始報數的人編號
void JosephRing(int n, int m, int k)
{
 LinkList p, r;    // p為當前結點,r為輔助結點,指向p的前驅結點
 LinkList list = NULL; //list為頭結點,未建立鏈表前是null 指標
 for(int i = 1; i <= n; i++) //建立迴圈隊列
 {
  p = (LinkList)malloc(sizeof(LNode));
  p->data = i;
  if(list == NULL)
   list = p;
  else
   r->next = p;
  r = p;  //建立鏈表中的每個節點
 }
 p->next = list; //建立好使隊列迴圈起來
 p = list; //使p指向前端節點
 
 //把當前指標移動到第一個報數的人
 for(i = 1; i < k; i++)
 {
  r = p;
  p = p->next;
 }
 
 //迴圈地刪除隊列結點
 while(p->next != p)
 {
  for(i = 1; i < m; i++)
  {
   r = p;
   p = p->next;
  }
  r->next=p->next;
  cout<<p->data<<endl;
  free(p);
  p=r->next;
 }
 cout<<endl<<"最終剩下的人為: "<<p->data<<endl;
}

int main()
{
 int n,m,k;
 cout<<"請輸入參數: n--總人數,m--每m個人出局,k--起始人編號: "<<endl;
 cout<<"要求: n>0; 1=<k<=n."<<endl;
 cin>>n>>m>>k;
 cout<<"出隊順序如下: "<<endl;
 JosephRing(n, m, k);  
 system("pause");
 return 0;
}

 

 

 

 

 

 

////////////////////////////////////////

 

 

#include <iostream>

using namespace std;

void yuesefu(int a[],int n,int m,int s)
{
 int k = n;   //總數
 int i = m-1+s-1;
 int c = 0,p;
 while(k > 1)
 {
  i%=n;
  cout<<"第"<<++c<<"個是:"<<a[i]<<endl;
  a[i] = 0;
  k--;
  p = 1;
  while(p<=m)
  {
   i++;
   if(a[i%n] != 0)
    p++;
  }
 }
 for(int i=0;i<n;i++)
  if(a[i] != 0)
  {
   cout<<"最後剩下的是:"<<a[i]<<endl;
   break;
  }
}

int main()
{
 int n,m,k;
 cout<<"n(總數)、m(報數)、k(起始人):";
 cin>>n>>m>>k;
 int *a = new int[n];
 for(int i=0;i<n;i++)
  a[i] = i+1;
 yuesefu(a,n,m,k);
 delete []a;
 a = 0;
 return 0;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.