用數學方法解約瑟夫環

2021-08-15 06:42:05 字數 2585 閱讀 3072

5.5.4  用數學方法解約瑟夫環

原文:上面編寫的解約瑟夫環的程式模擬了整個報數的過程,程式執行時間還可以接受,很快就可以出計算結果。可是,當參與的總人數n及出列值m非常大時,其運算速度就慢下來。例如,當n的值有上百萬,m的值為幾萬時,到最後雖然只剩2個人,也需要迴圈幾萬次(m的數量)才能確定2個人中下乙個出列的序號。顯然,在這個程式的執行過程中,很多步驟都是進行重複無用的迴圈。

那麼,能不能設計出更有效率的程式呢?

辦法當然有。其中,在約瑟夫環中,只是需要求出最後的乙個出列者最初的序號,而不必要去模擬整個報數的過程。因此,為了追求效率,可以考慮從數學角度進行推算,找出規律然後再編寫程式即可。

為了討論方便,先根據原意將問題用數學語言進行描述。

問題:將編號為0~(n–1)這n個人進行圓形排列,按順時針從0開始報數,報到m–1的人退出圓形佇列,剩下的人繼續從0開始報數,不斷重複。求最後出列者最初在圓形佇列中的編號。

下面首先列出0~(n–1)這n個人的原始編號如下:

根據前面曾經推導的過程可知,第乙個出列人的編號一定是(m–1)%n。例如,在41個人中,若報到3的人出列,則第乙個出列人的編號一定是(3–1)%41=2,注意這裡的編號是從0開始的,因此編號2實際對應以1為起點中的編號3。根據前面的描述,m的前乙個元素(m–1)已經出列,則出列1人後的列表如下:

根據規則,當有人出列之後,下乙個位置的人又從0開始報數,則以上列表可調整為以下形式(即以m位置開始,n–1之後再接上0、1、2……,形成環狀):

按上面排列的順序重新進行編號,可得到下面的對應關係:

即,將出列1人後的資料重新組織成了0~(n–2)共n–1個人的列表,繼續求n–1個參與人員,按報數到m–1即出列,求解最後乙個出列者最初在圓形佇列中的編號。

看出什麼規律沒有?對了,通過一次處理,將問題的規模縮小了。即,對於n個人報數的問題,可以分解為先求解(n–1)個人報數的子問題;而對於(n–1)個人報數的子問題,又可分解為先求[(n–1)–1]人個報數的子問題,……。

問題中的規模最小時是什麼情況?就是只有1個人時(n=1),報數到(m–1)的人出列,這時最後出列的是誰?當然只有編號為0這個人。因此,可設有以下函式:

那麼,當n=2,報數到(m–1)的人出列,最後出列的人是誰?應該是只有乙個人報數時得到的最後出列的序號加上m,因為報到m-1的人已出列,只有2個人,則另乙個出列的就是最後出列者,可用公式表示為以下形式:

通過上面的算式計算時,f(2)的結果可能會超過n值(人數的總數)。例如,設n=2,m=3(即2個人,報數到2時就出列),則按上式計算得到的值是:

一共只有2人參與,編號為3的人顯然沒有。怎麼辦?由於是環狀報數,因此當兩個人報完數之後,又從編號為0的人開始接著報數。根據這個原理,即可對求得的值與總人數n進行模運算,即:

5.5.4  用數學方法解約瑟夫環(2)

即,n=2,m=3(即有2個人,報數到3–1的人出列)時,迴圈報數最後乙個出列的人的編號為1(編號從0開始)。我們來推算一下,如下所示,當編號為0、1的兩個人迴圈報數時,編號為0的人報的數為0和2,當報到2(m–1)時,編號0出列,最後剩下編號為1的人,所以編號為1的人最後出列。

根據上面的推導過程,可以很容易推導出,當n=3時的公式:

同理,也可以推導出參與人數為n時,最後出列人員編號的公式:

其實,這就是乙個遞推公式,公式包含以下兩個式子:

有了這個遞推公式,再來設計程式就很簡單了,可以用遞迴的方法來設計程式,具體**如下:

#include 

<

stdio.h

>

int main(void)  

int josephus(int n,int m)  

在以上**中,定義了乙個遞迴函式josephus(),然後在主函式中呼叫這個函式進行   運算。

編譯執行以上程式,輸入n和m的值,可以很快得到最後出列人的編號,輸入n=8,m=3,得到的結果如圖5-19所示(注意編號是從0開始)。

使用遞迴函式會占用計算機較多的記憶體,當遞迴層次太深時可能導致程式不能執行,因此,也可以將程式直接編寫為以下的遞推形式:

#include 

<

stdio.h

>

int main(void)  

這段**執行的結果與遞迴程式執行結果完全相同。

可以看出,經過一些數學推導,最後總結出規律簡化程式,將幾十行的**縮減到幾行。更主要的是,程式執行的效率得到大大的提公升,省去了很多重複的迴圈,既使求解的n和m值很大,也不會成為問題

約瑟夫環問題(用數學方法推出計算公式)

基本問題描述 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,直到圓桌周圍的人全部出列。也類似於 殺人狂問題 通常解決這類問題時我們把編號從0 n 1,最後結果 1即為原問題...

約瑟夫問題的數學方法

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

約瑟夫問題的數學方法

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