演算法 約瑟夫環問題

2021-09-27 03:24:23 字數 3042 閱讀 6943

同步更新。歡迎移步體驗更好的閱讀效果。

約瑟夫問題是個著名的問題:n個人圍成一圈,第乙個人從1開始報數,報m的將被殺掉,下乙個人接著從1開始報。如此反覆,最後剩下乙個,求最後的勝利者。

例如只有三個人,把他們叫做a、b、c,他們圍成一圈,從a開始報數,假設報2的人被殺掉。

剛學資料結構的時候,我們可能用鍊錶的方法去模擬這個過程,n個人看作是n個鍊錶節點,節點1指向節點2,節點2指向節點3,……,節點n-1指向節點n,節點n指向節點1,這樣就形成了乙個環。然後從節點1開始1、2、3……往下報數,每報到m,就把那個節點從環上刪除。下乙個節點接著從1開始報數。最終鍊錶僅剩乙個節點。它就是最終的勝利者。

要模擬整個遊戲過程,時間複雜度高達o(nm),當n,m非常大(例如上百萬,上千萬)的時候,幾乎是沒有辦法在短時間內出結果的。

約瑟夫環是乙個經典的數學問題,我們不難發現這樣的依次報數,似乎有規律可循。為了方便匯出遞推式,我們重新定義一下題目。

問題:n個人編號為1,2,……,n,依次報數,每報到m時,殺掉那個人,求最後勝利者的編號。

這邊我們先把結論丟擲了。之後帶領大家一步一步的理解這個公式是什麼來的。

遞推公式:

f (n

,m)=

(f(n

−1,m

)+m)

%nf(n,m)=(f(n−1,m)+m)\%n

f(n,m)

=(f(

n−1,

m)+m

)%n

下面我們不用字母表示每乙個人,而用數字。

1 、2

、3、4

、5、6

、7、8

、9、10

、111、2、3、4、5、6、7、8、9、10、11

1、2、3、

4、5、

6、7、

8、9、

10、1

1表示11個人,他們先排成一排,假設每報到3的人被殺掉。

下圖表示這一過程(先忽視綠色的一行)

現在再來看我們遞推公式是怎麼得到的!

將上面**的每一行看成陣列,這個公式描述的是:倖存者在這一輪的下標位置

很神奇吧!現在你還懷疑這個公式的正確性嗎?上面這個例子驗證了這個遞推公式的確可以計算出勝利者的下標,下面將講解怎麼推導這個公式。

問題1:假設我們已經知道11個人時,勝利者的下標位置為6。那下一輪10個人時,勝利者的下標位置為多少?

:其實吧,第一輪刪掉編號為3的人後,之後的人都往前面移動了3位,勝利這也往前移動了3位,所以他的下標位置由6變成3。

問題2:假設我們已經知道10個人時,勝利者的下標位置為3。那上一輪11個人時,勝利者的下標位置為多少?

:這可以看錯是上乙個問題的逆過程,大家都往後移動3位,所以f(11

,3)=

f(10,

3)+3

f(11,3)=f(10,3)+3

f(11,3

)=f(

10,3

)+3。不過有可能陣列會越界,所以最後模上當前人數的個數,f(11

,3)=

(f(10

,3)+

3)%11

f(11,3)=(f(10,3)+3)\%11

f(11,3

)=(f

(10,

3)+3

)%11

:理解這個遞推式的核心在於關注勝利者的下標位置是怎麼變的。每殺掉乙個人,其實就是把這個陣列向前移動了m位。然後逆過來,就可以得到這個遞推式。

因為求出的結果是陣列中的下標,最終的編號還要加1

下面給出**實現:

int

cir(

int n,

int m)

return p+1;

}

如果要模擬出列順序, 怎麼辦?

可以使用linkedlist 來模擬, 因為linkedlist插入刪除效率比arraylist高。

public

static

void

cir(

int n,

int m)

int index = m -1;

// 初始化,index指向第乙個出列的人, 因為linkedlisted是從下標0開始, 所以要減1

while

(linkedlist.

size()

!=0)}

執行結果:

倖存者佇列[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

3號出列

倖存者佇列[1, 2, 4, 5, 6, 7, 8, 9, 10, 11]

6號出列

倖存者佇列[1, 2, 4, 5, 7, 8, 9, 10, 11]

9號出列

倖存者佇列[1, 2, 4, 5, 7, 8, 10, 11]

1號出列

倖存者佇列[2, 4, 5, 7, 8, 10, 11]

5號出列

倖存者佇列[2, 4, 7, 8, 10, 11]

10號出列

倖存者佇列[2, 4, 7, 8, 11]

4號出列

倖存者佇列[2, 7, 8, 11]

11號出列

倖存者佇列[2, 7, 8]

8號出列

倖存者佇列[2, 7]

2號出列

倖存者佇列[7]

7號出列

演算法 約瑟夫環問題

約瑟夫環 約瑟夫問題 是乙個數學的應用問題 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從第乙個人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,求最後乙個出圈的人的標號。void josef int n,int m n個人,m出列...

演算法題目 約瑟夫環問題

題目 0,1,n 1這n個數字排成乙個圓圈,從數字0開始每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後乙個數字。解法一 經典解法,用環形鍊錶模擬圓圈。這種方法每刪除乙個數字需要m步運算,總共有n個數字,因此總的時間複雜度是o mn 同時這種思路還需要乙個輔助鍊錶來模擬圓圈,其空間複雜度是o...

演算法題目 約瑟夫環問題

題目 0,1,n 1這n個數字排成乙個圓圈,從數字0開始每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後乙個數字。解法一 經典解法,用環形鍊錶模擬圓圈。這種方法每刪除乙個數字需要m步運算,總共有n個數字,因此總的時間複雜度是o mn 同時這種思路還需要乙個輔助鍊錶來模擬圓圈,其空間複雜度是o...