数据结构与算法--我们来玩丢手绢(约瑟夫环问题)
我們來玩丟手絹
- 昨天我們打撲克,今天我們丟手絹
- 丟手絹我們都知道這個(gè)游戲,他的由來由約瑟夫 (Josephus)提出來的
- 問題來了,我們就不殺呀殺了,還是丟手絹吧,讓n個(gè)人圍成一個(gè)環(huán),編號(hào)從1 ~n,從1 開始數(shù)當(dāng)數(shù)到第m個(gè)人時(shí)候,他從圈中離開,接著從m+1 個(gè)人數(shù),繼續(xù)m個(gè),在出局,依次復(fù)制這個(gè)過程,求最后一個(gè)出局的人是幾號(hào)?
方案一,環(huán)形鏈表
- 題目明顯提到了數(shù)字的圓圈,自然我們可以構(gòu)造一個(gè)這樣類似的數(shù)據(jù)結(jié)構(gòu)去模擬這種過程。在常用數(shù)據(jù)結(jié)構(gòu)中,n個(gè)階段的環(huán)形鏈表很好的重現(xiàn)了這種模式
- 我們先構(gòu)建n個(gè)節(jié)點(diǎn)的鏈表,每次刪除第m個(gè)元素,當(dāng)環(huán)形鏈表中元素只剩下一個(gè)時(shí)候得到解
- 算法分析:
- 鏈表頭開始,指定位置position,鏈表尾指定位置before
- 循環(huán)m次,分別將position與before都前移m-1次,得到指定位置
- 刪除psition位置節(jié)點(diǎn),讓position指向position.next,統(tǒng)計(jì)個(gè)數(shù)count = n-1
- 繼續(xù)以上步驟
- 以上步驟中循環(huán)次數(shù) (n-1)*m次因此時(shí)間復(fù)雜度應(yīng)該是O(nm),當(dāng)m特別大40000 ,時(shí)間復(fù)雜度會(huì)異常的大,非常慢
- 優(yōu)化:可以通過取余操作,直接計(jì)算出m次循環(huán)在 鏈表中實(shí)際的步驟,將 m % count -1得到實(shí)際步驟,當(dāng)m是大數(shù)時(shí)候,優(yōu)化效果明顯
- 如上分析有如下代碼:
- 以上算法時(shí)間復(fù)雜度O(nm), 空間復(fù)雜度O(n)
約瑟夫環(huán)問題
-
約瑟夫環(huán)問題最終還是一個(gè)數(shù)學(xué)問題(數(shù)學(xué)能救命),我們大概來推導(dǎo)一下
-
首先定義一個(gè)關(guān)于n 和 m的方法記為f(n, m),表示每次在n個(gè)數(shù)字0~n-1中每次刪除第m個(gè)數(shù)字,最后剩下的數(shù)字
-
第一個(gè)刪除的數(shù)字用n,m表示:(m-1)% n , 當(dāng)且僅當(dāng) m>0 n>1的時(shí)候,我們記為k =(m-1)% n 如下圖
-
接著下一次遍歷是從k+1 開始的現(xiàn)有數(shù)字總共 n-2個(gè),k+1排第一,也就有如下圖所示的順序
-
我們記錄第一個(gè)刪除后的值是 d(n-1, m),與之前的 f(n, m)是同一個(gè)規(guī)則刪除,那么他們最終的結(jié)果肯定是一樣的,那么就有 d(n-1, m) = f(n, m)
-
也就是我們將k+1當(dāng)成是當(dāng)前的第0 位置,k-1是當(dāng)前最后的位置,也就是 n-2,那么我們依次推導(dǎo)出各個(gè)的位置,k+2是第一個(gè)位置
- n-2之后有n-1,在加上k個(gè)數(shù) (n-2)-(k+2)+1=n-k-3
- n-2 之后有k1個(gè)數(shù),(n-2)-(k+1)+1 = n-k+2
- 同理 0 則是 n-k+1
- 1 是 n-k
- k-1則是n-2
- 用下圖對(duì)應(yīng)關(guān)系展示
-
如上圖,上部分是數(shù)據(jù)值,下部分是對(duì)應(yīng)的位置,我們可以定義兩個(gè)集合:
- A集合是上部分?jǐn)?shù)據(jù)值
- B集合是下部分位置值
- A,B為非空集, 若存在對(duì)應(yīng)法則f(), 使得對(duì)每個(gè) x∈y 都有唯一確 y∈B與之對(duì)應(yīng), 則稱對(duì)應(yīng)法則f()為A到B的映射
-
如果我們找到了數(shù)據(jù)值與 位置值的映射關(guān)系函數(shù),是不是可以直接通過計(jì)算得到下一步中需要?jiǎng)h除的數(shù)據(jù)
-
此處,我們定義映射為p,推導(dǎo)出p(x) = (x-k-1)%n,映射的逆映射就是p(x) = (x+k+1)%n
-
根據(jù)以上映射規(guī)則,映射之前的寫中最后剩下的數(shù)字 d(n-1, m) = p[f(n-1)+m] = [f(n-1,m) +k +1]%n,將k=(m-1)%n 得到f(n,m) = d(n-1,m)= [f(n-1,m) +k +1]%n
-
那么我們得到一個(gè)遞推公式
- n =1 f(n, m) = 0
- n>1 f(n, m) = [f(n-1, m)+m ]%n
-
有了如上公式我們很自然直接遞歸就能得到第n次的結(jié)果
-
如上分析有如下代碼:
- 以上遞歸實(shí)現(xiàn)當(dāng)n, m值非常大時(shí)候幾乎不可用,情況通之前文章斐波那契數(shù)量原因一樣
優(yōu)化方案三
- 動(dòng)態(tài)規(guī)劃解法:在以上推導(dǎo)基礎(chǔ)上用一個(gè)循環(huán)解決
- 可以看出,以上實(shí)現(xiàn)思路分析非常復(fù)雜,但是代碼簡單時(shí)間復(fù)雜度O(n),空間復(fù)雜度O(1),遠(yuǎn)優(yōu)于第一,第二解法
上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–判斷撲克牌是否順子
下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–這個(gè)需求很簡單怎么實(shí)現(xiàn)我不管(發(fā)散思維)
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法--我们来玩丢手绢(约瑟夫环问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 艾灸拉肚子是怎么回事
- 下一篇: 面部刮痧后长痘痘怎么回事