1. 程式人生 > >約瑟夫問題,從o(n*m)到o(n)乃至o(m)的演算法複雜度進階

約瑟夫問題,從o(n*m)到o(n)乃至o(m)的演算法複雜度進階

問題描述:

n個人(編號1~n),從1開始報數,報到m的退出,剩下的人繼續從1開始報數。按順序輸出列者編號。


1.演算法複雜度為o(n*m)

使用連結串列進行模擬整個遊戲過程


2.演算法複雜度為o(n)

將n個人按照從0~n進行編號,出列的第一個人編號是m%n-1,將剩下的n-1個人組成一個新的約瑟夫環(從編號為k=m%n的人重新編號):

   k   k+1   k+2   ... n-2, n-1, 0, 1, 2, ... k-2
現在我們把他們的編號做一下轉換:
k     --> 0
k+1 --> 1
...
...
k-1 --> n-1

變換後就完完全全成為了(n-1)個人報數的子問題,假如我們知道這個子問題的解:例如x是最終的勝利者,那麼根據上面這個表把這個x變回去不剛好就是n個人情況的解嗎?!!變回去的公式很簡單,相信大家都可以推出來:x'=(x+k)%n


如何知道(n-1)個人報數的問題的解?對,只要知道(n-2)個人的解就行了。(n-2)個人的解呢?當然是先求(n-3)的情況 ---- 這顯然就是一個倒推問題!好了,思路出來了,下面寫遞推公式:


令f[i]表示i個人玩遊戲報m退出最後勝利者的編號,最後的結果自然是f[n]


遞推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;   (i>1)


有了這個公式,我們要做的就是從1-n順序算出f[i]的數值,最後結果是f[n]。因為實際生活中編號總是從1開始,我們輸出f[n]+1


由於是逐級遞推,不需要儲存每個f[i],程式也是異常簡單: 

#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 ("The winner is %d\n", s+1);
}

3.演算法複雜度為O(m)(m<<n)

事實上,如果我們觀察上述演算法中的變數s,他的初始值為第一個出圈人的編號,但在迴圈的過程中,我們會發現它常常處在一種等差遞增的狀態,我來看這個式 子:s=(s+m)%i;,可以看出,當i比較大而s+m-1比較小的時候,s就處於一種等差遞增的狀態,這個等差遞增的過程並不是必須的,可以跳過。

我們設一中間變數x,列出如下等式:
s+m*x–1=i+x

解出x,令s=s+m*x,將i+x直接賦值給 i,這樣就跳過了中間共x重的迴圈,從而節省了等差遞增的時間開銷。可是其中求出來的x+i可能會超過n,這樣的結果事實上已經告訴我們此時可以直接結束演算法了。


整個演算法的C語言描述如下:

long Josephus(long n,long m,long k) //分別為:人數,出圈步長,起使報數位置,
{
    if (m == 1)k = k == 1 ? n : (k + n - 1) % n;
    else
    {
        for (long i = 1; i <= n; i++)
        {
            if ((k + m) < i)
            {
                x = (i - k + 1) / (m - 1) - 1;
                if (i + x < n)
                {
                    i = i + x;
                    k = (k + m * x);
                }
                else
                {
                    k = k + m * (n - i) ;
                    i = n;
                }
            }
            k = (k + m - 1) % i + 1;
        }
    }
    return k; //返回最後一人的位置
}


該演算法的演算法複雜度在m<n時已經與一個圈中的人數n沒有關係了,即使在n=2000000000,m=3,s=1的情況下,也只做了54次迴圈,事 實上,大多數的情況都是m<n,且m相對來說很小,此時,這個演算法的複雜度僅為O(m);但當m>=n時,用方程求出的值不能減少迴圈重數,演算法複雜度仍為O(n)。

相關連結:http://www.cnblogs.com/void/archive/2011/04/21/2024377.html