約瑟夫環問題——數學證明 乾貨!通俗易懂

前言

看了好多博客抄來抄去,沒有一個把事情說清楚的。當然也可能是我蠢看不懂人家高深的論述...

最終找到一篇我能看懂的帖子,現在用我的思路複述一遍...

如果你看我的帖子看不明白,可以選擇移步原帖 地址:https://www.cnblogs.com/cmmdc/p/7216726.html

我相信這篇你一定能看懂,拿上紙筆跟我一起推公式,廢話不多說...

什麼是約瑟夫環問題

現在有n個人,從1編號至n。編號爲1的人開始報數,當報到key時,報數爲key的人出局。從key+1個人的位置繼續從1開始報數。一直循環。試問最後一個出局人的人是誰?

 

乾貨! 乾貨! 乾貨!

我們把這n個人從0開始編號。這樣做是因爲key是有可能大於n的,所以出局的人編號應該是key%n,顯然key也可能等於n,這就導致key%n=0 所以爲了描述方便,我們從0開始編號,所以對我們來說 數到key-1的那個人出列

所以現在這些人的順序是 0 1 2  ... n-2 n-1

顯然出局人的序號將是(key-1)%n。那麼下一次報0的人在原序列中的序號就將是 key%n,記爲k1

我們重新進行排列,將k1作爲第一個人, 剩下的n-1個人將得到如下序列

k1, k1+1, k1+2, ... , 0, 1, 2 ... k1-3, k1-2 (k1-1已經出局了)

這樣就清楚多了,k1將從0開始報數,於是報數序列如下

0, 1, 2, ... n-3, n-2

於是我們發現了這樣的對應關係

新編號 <--> 舊編號

  0       <-->   k1

  1       <-->   k1+1

  2       <-->   k1+2

  ...

根據規律我們得到

  n-2 <--> k1+(n-2)

但是注意k1+(n-2)是可能大於當前總人數的,所以我們需要對它取模,於是真正的對應關係是

  n-2 <--> (k1+(n-2))%n (這裏n是舊隊列的總人數)

如果我們假設新編號是ans,那麼新編號和舊編號之間的對應關係就是

  ans <--> (k1+ans)%n

好了不難看到,我們得到的新隊列並重新編號之後,現在的問題是

求這個 0, 1, 2, ... n-3, n-2 隊列中出局的是誰

事實上這是一個n-1階約瑟夫環問題。我們已經得到了遞推公式,但如果你還沒明白的話我們再來重複上面的過程

在這樣的隊列中 出局的人將是 (key-1)%n2 (這裏n2是指當前隊列總人數,爲了和上面的n區分)。我們又可以知道下一個報0的人的編號將是key%n2,記這個數爲k2

於是我們將k2放在隊首

k2, k2+1, k2+2, ... 0, 1, 2,  k2-3, k2-2 (k2-1已經出局了)

對應每個人將要報的口號將是

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

所以這又變成了一個n-2階約瑟夫環問題

好了,禁止套娃

現在我們來考慮一下,我們已經將新隊列中的口號和舊隊列中的口號建立了關係,那麼我們只需要知道新隊列中的口號就可以求出舊隊列中的口號。現在我們假設新隊列中的口號爲ans,那麼對應舊隊列中的口號將是 (ki + ans)%ni

遞推公式怎麼來的呢?

假設在新隊列(n-1階問題)中出局的人的編號是 out ,那麼舊隊列(n階問題)中他的編號就是 (kn+out)%nn(第二個n是角標)

而kn實際上就是m%n,(還記得我們之前怎麼定義的k嗎)

!於是n階Joseph問題的解將是n-1階Joseph問題的解通過 (kn + Joseph(n-1))%nn(第二個n是角標) 得到的!

 所以我們就有了如下遞推公式

f(n,m)=(f(n-1,m)+m%n)%n

這個公式已經不影響你寫代碼了。但是它還是可以化簡一下。

我們知道  (f(n-1,m)+m%n)%n = (f(n-1,m)%n,m%n%n)%n

又有右半部分 (f(n-1,m)%n + m%n%n)%n = (f(n-1,m)%n + m%n)%n = (f(n-1,m)+m)%n

最終我們的遞推關係式如下

f(n,m) = (f(n-1,m)+m)%n

 

附上Java代碼

當然業務代碼裏我們最終得到的值要加上 1,因爲我們報數的時候是從0開始報的。

    public int joseph(int sum,int word){
        if (sum == 1){
            return 0;
        }
        return (joseph(sum-1,word)+word)%sum;
    }

最後想說的

又回到了做高中數學題的感覺。但這總比公式來得不明不白強不是? 數學方法還是很好理解的,能推導就不要用嘴說。

完結撒花~ Yes!明天更點JVM的內容吧...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章