約瑟夫環問題 ( 最簡單的數學解法)

基本問題描述:
已知n我的(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲1的人開始報數,數到m的那我的出列;他的下一我的又從1開始報數,數到m的那我的又出列;依此規律重複下去,直到圓桌周圍的人所有出列。(也相似於變態殺人狂問題)一般解決這類問題時咱們把編號從0~n-1,最後結果+1即爲原問題的解。一般,咱們會要求輸出最後一位出列的人的序號。那麼這裏主要研究的是最後一個出列的人的序號要怎麼肯定。html

當n,m數據量很小的時候,咱們能夠用循環鏈表模擬約瑟夫環的過程。當模擬到人數等於1的時候,輸出剩下的人的序號便可。
具體解法這種方法每每實現起來比較簡單,並且也很容易理解。可是時間複雜度倒是很糟糕的,達到了O(nm),這樣的話,其實在n,m比較大的時候(nm達到10^8或者更大),那麼要得出結果每每須要耗費很長的時間,可是咱們能夠運用一點數學上的技巧,將最後結果推導出來。c++

爲了簡化出列的過程:
首先咱們把這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,那麼咱們還能夠將該式進行一下簡單的化簡:code

當m<n時,易得上式可化簡爲:(ans + m)% nhtm

而當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便可。blog

代碼:遞歸

#include <stdio.h>
int main()
{
    int n, m, i, s = 0;
    printf ("N M = ");
    scanf("%d%d", &n, &m);
    for (i = 2; i <= n; i++)
    {
        s = (s + m) % i;
        printf("%d\n",s);
    }
    printf ("\nThe winner is %d\n", s+1);
}
相關文章
相關標籤/搜索