約瑟夫環問題(遞推公式)

2021-09-19 13:38:37 字數 3300 閱讀 3036

約瑟夫環問題在==《具體數學》==一書上講得十分詳細

基本問題描述:

已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列;他的下乙個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。(也類似於**殺人狂問題)通常解決這類問題時我們把編號從0~n-1,最後結果+1即為原問題的解。通常,我們會要求輸出最後一位出列的人的序號。那麼這裡主要研究的是最後乙個出列的人的序號要怎麼確定。

當n,m資料量很小的時候,我們可以用迴圈鍊錶模擬約瑟夫環的過程。當模擬到人數等於1的時候,輸出剩下的人的序號即可。

具體解法這種方法往往實現起來比較簡單,而且也很容易理解。但是時間複雜度卻是很糟糕的,達到了o(nm),這樣的話,其實在n,m比較大的時候(nm達到10^8或者更大),那麼要得出結果往往需要耗費很長的時間,但是我們可以運用一點數學上的技巧,將最後結果推導出來。

為了簡化出列的過程:

首先我們把這n個人的序號編號從0~n-1(理由很簡單,由於m是可能大於n的,而當m大於等於n時,那麼第乙個出列的人編號是m%n,而m%n是可能等於0的,這樣編號的話能夠簡化後續出列的過程),當數到m-1的那個人出列,因此我們編號完成之後,開始分析出列的過程:

第一次出列:

一開始的時候,所有人的編號排成序列的模式即為:

0,1,2,3,4,5…n-2,n-1

那麼第一次出列的人的編號則是(m-1)%n1,那麼在第乙個人出列之後,從他的下乙個人又開始從0開始報數,為了方便我們設k1 = m%n1(n1為當前序列的總人數)那麼在第乙個人出列之後,k1則是下一次新的編號序列的首位元素,那麼我們得到的新的編號序列為:

k1,k1+1,k1+2,k1+3…n-2,n-1,0,1,2…k1-3,k1-2 (k1-1第一次已出列)

那麼在這個新的序列中,第乙個人依舊是從0開始報數,那麼在這個新的序列中,每個人報的相應數字為:

0,1,2,3…n-2

那麼第二次每個人報的相應數字與第一次時自己相應的編號對應起來的關係則為:

0 --> k1

1 --> k1+1

2 --> k1+2

…n-2 —> (k1+n-2)%n1(n1為當前序列的總人數,因為是迴圈的序列,k1+n-1可能大於總人數)

那麼這時我們要解決的問題就是n-1個人的報數問題(即n-1階約瑟夫環的問題)

可能以上過程你還是覺得不太清晰,那麼我們重複以上過程,繼續推導剩餘的n-1個人的約瑟夫環的問題:

那麼在這剩下的n-1個人中,我們也可以為了方便,將這n-1個人編號為:

0,1,2,3,4…n-2

那麼此時出列的人的編號則是(m-1) % n2(n2為當前序列的總人數),同樣的我們設k2 = m % n2,那麼在這個人出列了以後,序列重排,重排後新的編號序列為:

k2,k2+1,k2+2,k2+3…n-2,n-1,0,1,2…k2-3,k2-2 (k2-1第一次已出列)

那麼在這個新的序列中,第乙個人依舊是從1開始報數,那麼在這個新的序列中,每個人報的相應數字為:

1,2,3,4…n-2

那麼這樣的話是不是又把問題轉化成了n-2階約瑟夫環的問題呢?

後面的過程與前兩次的過程一模一樣,那麼遞迴處理下去,直到最後只剩下乙個人的時候,便可以直接得出結果

當我們得到乙個人的時候(即一階約瑟夫環問題)的結果,那麼我們是否能通過一階約瑟夫環問題的結果,推導出二階約瑟夫環的結果呢?

借助上面的分析過程,我們知道,當在解決n階約瑟夫環問題時,序號為k1的人出列後,剩下的n-1個人又重新組成了乙個n-1階的約瑟夫環,那麼

假如得到了這個n-1階約瑟夫環問題的結果為ans(即最後乙個出列的人編號為ans),那麼我們通過上述分析過程,可以知道,n階約瑟夫環的結果

(ans + k)%n(n為當前序列的總人數),而k = m%n

則有:n階約瑟夫環的結果

(ans + m % n)%n,那麼我們還可以將該式進行一下簡單的化簡:

當m而當m>=n時,那麼上式則化簡為:(ans % n + m%n%n)% n

即為:(ans % n + m%n)% n

而 (ans + m)% n = (ans % n + m%n)% n

因此得證

(ans + m % n)%n = (ans + m)% n

這樣的話,我們就得到了遞推公式,由於編號是從0開始的,那麼我們可以令

f[1] = 0; //當乙個人的時候,出隊人員編號為0

f[n] = (f[n-1] + m)%n //m表示每次數到該數的人出列,n表示當前序列的總人數

而我們只需要得到第n次出列的結果即可,那麼不需要另外宣告陣列儲存資料,只需要直接乙個for迴圈求得n階約瑟夫環問題的結果即可

由於往往現實生活中編號是從1-n,那麼我們把最後的結果加1即可。

公式

#include

#define inf 0x3f3f3f3f

#define d(x) cout << (x) << endl

#pragma gcc diagnostic error "-std=c++11"

using namespace std;

typedef

long

long ll;

const

int mod =

1000000009

;const

int n =

1e6;

intmain()

printf (

"%d\n"

, s+1)

;}

模擬

#include

#define inf 0x3f3f3f3f

#define d(x) cout << (x) << endl

#pragma gcc diagnostic error "-std=c++11"

using namespace std;

typedef

long

long ll;

const

int mod =

1000000009

;const

int n =

1e6;

set<

int> s;

int vis[n]

;int

main()

num--

;continue;}

if(num == k)

if(i == n)

}for

(int i =

1; i <= n; i++)}

printf

("\n");

return0;

}

約瑟夫環 遞推公式

遞推公式 f n,m f n 1,m m n f n,m f n 1,m m nf n 1,m 1 2 3 4 5 6 7 8 9 1001 2345 6789 1234 5678 91045 6789 10127 89101 24510 1245 7845 78101 81014 5458 101...

約瑟夫環遞推公式推導

一共有 n 個人,編號為1,2,n,這些人圍成乙個圈子,然後指定乙個數 m 從 1 號開始,數到 m 的人出列,並且下乙個人重新開始由 1 計數,問最後乙個出列的人是幾號?1.通常這類問題會想到使用鍊錶解決,每 m 個就刪除乙個節點 2.也可以用迴圈陣列的方式,對陣列的下標取模 對於以上兩種方法都不...

知識點 約瑟夫環遞推公式

傳送 遞推公式 f 1 0,f n f n 1 k n。f i 是有i個人時,勝出的那個人的編號 編號從0 n 1 我一開始一直沒理解這個遞推是怎麼來的,後來終於理解了 假設問題是從n個人編號分別為0.n 1,取第k個,則第k個人編號為k 1的淘汰,剩下的編號為 0,1,2,3.k 2,k,k 1,...