約瑟夫環問題的幾種解法

2021-07-09 04:41:29 字數 2144 閱讀 9643

一、問題的來歷

據說著名猶太歷史學家 josephus有過以下的故事:在羅馬人占領喬塔帕特後,39個猶太人與josephus及他的朋友躲在乙個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了乙個自殺方式,41個人排成乙個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下乙個重新報數,直到所有人都自殺身亡為止。然而josephus 和他的朋友並不想遵從。問題是,給定了總人數n和報數值m,一開始要站在什麼地方才能避免被處決?josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲。

二、問題的基本描述

n個人圍成圈,依次編號為1、2、3、...、n,從1號開始依次報數,當報到m時,報m的人退出,下乙個人重新從1報起,當報到m時,報m的人退出,如此迴圈下去,問最後剩下的那個人的編號是多少?

三、解題方法

(一)模擬法

這個問題的解決,最容易想到的方法就是模擬法,利用乙個迴圈鍊錶,節點的數值部分儲存整數1至n,每次遍歷m步,把第m個節點數值列印輸出後刪除該節點,如此迴圈下去,直至只剩乙個節點,(只剩乙個節點的判斷方法是:節點的指向節點自己,也就是p->next=p),節點的數值部分就是最後那個人的編號。下面是c語言程式:

#include #include typedef struct node  /*宣告乙個鍊錶節點*/

node;

node* creatnode(int x) /*建立鍊錶節點的函式*/

node* creatjoseph(int n) /*建立環形鍊錶,存放整數1到n*/

q->next=head;

return head;

}void runjoseph(int n,int m) /*模擬執行約瑟夫環,每數到乙個數,將它從環形鍊錶中摘除,並列印出來*/

q=p->next;

p->next=q->next;

p=p->next;

printf("%d--",q->number);

free(q);

}printf("\n最後剩下的數為:%d\n",p->number);

}int main()

(二)遞迴法

要想用到遞迴法就必須找到f(n)和f(n-1)之間的關係,那麼約瑟夫環有沒有這樣乙個規律關係在呢,答案是有的。

我們假設n個人,報數到m的退出,最後剩下人的編號為x。那麼第一次報數後,編號為m的人退出,那麼剩下的人從編號為m+1繼續報數,如果我們把m+1看成1,m+2看成2,....,n看成n-m,1看成是1-m+n,2看成是2-m+n,...,m-1看成是(m-1)-m+n也就是n-1,那麼這就變成了乙個n-1個人報數為m的約瑟夫環的問題,而且這裡最後剩下人就是原來編號為x的那個人,按前面的對應關係f(n)=(f(n-1)+m)%n,這裡有個例外,就是如果x=n的話,就會出現f(n)=n,而(f(n-1)+m)%n=((n-m)+m)%n=n%n=0,所以我們把編號加點小技巧,如果n個人編號從0編到n-1,那麼f(n)=(f(n-1)+m)%n成立,如果換算成1到n編號,f(n)-1=((f(n-1)-1)+m)%n,也就是f(n)=((f(n-1)-1)+m)%n+1。

有了f(n)=(f(n-1)-1+m)%n+1這個公式,另外我們知道,當n=2時,m為奇數時最後留下的是2,m為偶數時最後留下的是1,我們就可以寫出遞迴程式了,下面是遞迴法的c語言程式:

#include int joseph(int n,int m)/*計算約瑟夫環的遞迴函式*/

else

}int main()

(三)迭代法

有遞迴法,我們看看有沒有相應的迭代法,用的還是剛才的那個公式,方法就是從總人數為2開始一步一步推導到總人數為n時最後應該留下誰。下面是迭代法的c語言程式:

#include int josephus(int n,int m)/*計算約瑟夫環問題的迭代法函式*/

return y;}

int main()

四、總結

模擬法的好處是,可以模擬過程,可以將出列人員按次序輸出出來,不足之處是演算法複雜度為o(mn),當m和n數值較大時,計算量太大。遞迴法和迭代法的好處是演算法複雜度為o(n),計算量小,運算速度快,但無法模擬過程,無法按次序輸出先後的出列人員。

約瑟夫環問題數學解法

首先一開始的序列 序列1 1,2,3,4,n 2,n 1,n 此時出佇列的第乙個人,位置為k,號碼肯定是m n。這個應該沒有問題,也就是取餘操作使得陣列類似能夠有迴圈的功能。此時序列2 1,2,3,4,k 1,k 1,n 2,n 1,n 此時k出佇列,序列2中為n 1個人了。根據序列2,得到序列3 ...

約瑟夫環O N 解法

無論是用鍊錶實現還是用陣列實現都有乙個共同點 要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達o nm 當n,m非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...

約瑟夫環問題的數學解法

無論是用鍊錶實現還是用陣列實現都有乙個共同點 要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達o nm 當n,m非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...