同步更新。歡迎移步體驗更好的閱讀效果。
約瑟夫問題是個著名的問題: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...