遞歸算法的時間複雜度分析

 在算法分析中,當一個算法中包含遞歸調用時,其時間複雜度的分析會轉化爲一個遞歸方程求解。實際上,這個問題是數學上求解漸近階的問題,而遞歸方程的形式多種多樣,其求解方法也是不一而足,比較常用的有以下四種方法:

    (1)代入法(Substitution Method)
    
    代入法的基本步驟是先推測遞歸方程的顯式解,然後用數學歸納法來驗證該解是否合理。
    
    (2)迭代法(Iteration Method)
    
    迭代法的基本步驟是迭代地展開遞歸方程的右端,使之成爲一個非遞歸的和式,然後通過對和式的估計來達到對方程左端即方程的解的估計。
    
    (3)套用公式法(Master Method)
    
    這個方法針對形如“T(n) = aT(n/b) + f(n)”的遞歸方程。這種遞歸方程是分治法的時間複雜性所滿足的遞歸關係,即一個規模爲n的問題被分成規模均爲n/b的a個子問題,遞歸地求解這a個子 問題,然後通過對這a個子間題的解的綜合,得到原問題的解。
    
    (4)差分方程法(Difference Formula Method)

    可以將某些遞歸方程看成差分方程,通過解差分方程的方法來解遞歸方程,然後對解作出漸近階估計。
    
    下面就以上方法給出一些例子說明。
        
    一、代入法
    
    大整數乘法計算時間的遞歸方程爲:T(n) = 4T(n/2) + O(n),其中T(1) = O(1),我們猜測一個解T(n) = O(n2 ),根據符號O的定義,對n>n0,有T(n) < cn2 - eO(2n)(注意,這裏減去O(2n),因其是低階項,不會影響到n足夠大時的漸近性),把這個解代入遞歸方程,得到:
    
    T(n) =  4T(n/2) + O(n)
           ≤ 4c(n/2)2 - eO(2n/2)) + O(n)
           =  cn- eO(n) + O(n)
           ≤ cn2 
    
    其中,c爲正常數,e取1,上式符合 T(n)≤cn2 的定義,則可認爲O(n2 )是T(n)的一個解,再用數學歸納法加以證明。
    
    二、迭代法

    某算法的計算時間爲:T(n) = 3T(n/4) + O(n),其中T(1) = O(1),迭代兩次可將右端展開爲:
    
    T(n) = 3T(n/4) + O(n)
         = O(n) + 3( O(n/4) + 3T(n/42 ) )
         = O(n) + 3( O(n/4) + 3( O(n/42 ) + 3T(n/43 ) ) )
         
    從上式可以看出,這是一個遞歸方程,我們可以寫出迭代i次後的方程:
    
    T(n) = O(n) + 3( O(n/4) + 3( O(n/42 ) + ... + 3( n/4i + 3T(n/4i+1 ) ) ) )
    
    當n/4i+1 =1時,T(n/4i+1 )=1,則
    
    T(n) = n + (3/4) + (32 /42 )n + ... + (3i /4i )n + (3i+1 )T(1)
         < 4n + 3i+1 
         
    而由n/4i+1 =1可知,i<log4 n,從而
    
    3i+1 ≤ 3log4 n+1 = 3log3 n*log4 3 +1 = 3nlog4 3
    
    代入得:
    
    T(n) < 4n + 3nlog4 3,即T(n) = O(n)。
    
    三、套用公式法
    
    這個方法爲估計形如:

  T(n) = aT(n/b) + f(n)

  其中,a≥1和b≥1,均爲常數,f(n)是一個確定的正函數。在f(n)的三類情況下,我們有T(n)的漸近估計式:

    1.若對於某常數ε>0,有f(n) = O(nlogb a-ε ),則T(n) = O(nlogb a )
    
    2.若f(n) = O(nlogb a ),則T(n) = O(nlogb a *logn)
    
    3.若f(n) = O(nlogb a+ε ),且對於某常數c>1和所有充分大的正整數n,有af(n/b)≤cf(n),則T(n)=O(f(n))。
    
    設T(n) = 4T(n/2) + n,則a = 4,b = 2,f(n) = n,計算得出nlogb a = nlog2 4 = n2 ,而f(n) = n = O(n2-ε ),此時ε= 1,根據第1種情況,我們得到T(n) = O(n2 )。
    
    這裏涉及的三類情況,都是拿f(n)與nlogb a 作比較,而遞歸方程解的漸近階由這兩個函數中的較大者決定。在第一類情況下,函數nlogb a 較大,則T(n)=O(nlogb a );在第三類情況下,函數f(n)較大,則T(n)=O(f (n));在第二類情況下,兩個函數一樣大,則T(n)=O(nlogb a *logn),即以n的對數作爲因子乘上f(n)與T(n)的同階。
    
    但上述三類情況並沒有覆蓋所有可能的f(n)。在第一類情況和第二類情況之間有一個間隙:f(n)小於但不是多項式地小於nlogb a ,第二類與第三類之間也存在這種情況,此時公式法不適用。



二 

  遞歸函數時間複雜度分析

 

(1) 遞歸執行過程 
   例子:求N!。 
    這是一個簡單的"累乘"問題,用遞歸算法也能解決。 
    n! = n * (n - 1)!   n > 1 
    0! = 1, 1! = 1      n = 0,1 
    因此,遞歸算法如下: 
   
Java代碼 
fact(int n) {  
    if(n == 0 || n == 1)   
         return 1;  
        else   
             return n * fact(n - 1);  
    }  
    以n=3爲例,看運行過程如下: 
    fact(3) ----- fact(2) ----- fact(1) ------ fact(2) -----fact(3) 
    ------------------------------>  ------------------------------> 
                遞歸                            回溯 
  遞歸算法在運行中不斷調用自身降低規模的過程,當規模降爲1,即遞歸到fact(1)時,滿足停止條件停止遞歸,開始回溯(返回調用算法)並計算,從fact(1)=1計算返回到fact(2);計算2*fact(1)=2返回到fact(3);計算3*fact(2)=6,結束遞歸。 
   算法的起始模塊也是終止模塊。 
(2) 遞歸實現機制 
    每一次遞歸調用,都用一個特殊的數據結構""記錄當前算法的執行狀態,特別地設置地址棧,用來記錄當前算法的執行位置,以備回溯時正常返回。遞歸模塊的形式參數是普通變量,每次遞歸調用得到的值都是不同的,他們也是由""來存儲。 
(3) 遞歸調用的幾種形式 
    一般遞歸調用有以下幾種形式(其中a1a2b1b2k1k2爲常數)。 
   <1> 直接簡單遞歸調用: f(n) {...a1 * f((n - k1) / b1); ...}; 
    
   <2> 直接複雜遞歸調用: f(n) {...a1 * f((n - k1) / b1); a2 * f((n - k2) / b2); ...}; 
    <3> 間接遞歸調用:  f(n) {...a1 * f((n - k1) / b1); ...}, 
                        g(n) {...a2 * f((n - k2) / b2); ...}。 
2. 遞歸算法效率分析方法 
   遞歸算法的分析方法比較多,最常用的便是迭代法。 
  迭代法的基本步驟是先將遞歸算法簡化爲對應的遞歸方程,然後通過反覆迭代,將遞歸方程的右端變換成一個級數,最後求級數的和,再估計和的漸進階。 
  <1> 例:n! 
       算法的遞歸方程爲: T(n) = T(n - 1) + O(1); 
       迭代展開: T(n) = T(n - 1) + O(1) 
                       = T(n - 2) + O(1) + O(1) 
                       = T(n - 3) + O(1) + O(1) + O(1) 
                       = ...... 
                       = O(1) + ... + O(1) + O(1) + O(1) 
                       = n * O(1) 
                       = O(n) 
      這個例子的時間複雜性是線性的。 
<2> 例:如下遞歸方程: 
      
      T(n) = 2T(n/2) + 2, 且假設n=2k次方。 
      T(n) = 2T(n/2) + 2 
           = 2(2T(n/2*2) + 2) + 2 
           = 4T(n/2*2) + 4 + 2 
           = 4(2T(n/2*2*2) + 2) + 4 + 2 
           = 2*2*2T(n/2*2*2) + 8 + 4 + 2 
           = ... 
           = 2(k-1)次方 * T(n/2(i-1)次方) + $(i:1~(k-1))2i次方 
           = 2(k-1)次方 + (2k次方)  - 2 
           = (3/2) * (2k次方) - 2 
           = (3/2) * n - 2 
           = O(n) 
      這個例子的時間複雜性也是線性的。 
<3> 例:如下遞歸方程: 
      
      T(n) = 2T(n/2) + O(n), 且假設n=2k次方。 
      T(n) = 2T(n/2) + O(n) 
           = 2T(n/4) + 2O(n/2) + O(n) 
           = ... 
           = O(n) + O(n) + ... + O(n) + O(n) + O(n) 
           = k * O(n) 
           = O(k*n) 
           = O(nlog2n) //2爲底 
     
      一般地,當遞歸方程爲T(n) = aT(n/c) + O(n), T(n)的解爲: 
      O(n)          (a<c && c>1) 
      O(nlog2n)     (a=c && c>1) //2爲底 
      O(nlogca)     (a>c && c>1) //n(logca)次方,以c爲底 
   上面介紹的3種遞歸調用形式,比較常用的是第一種情況,第二種形式也有時出現,而第三種形式(間接遞歸調用)使用的較少,且算法分析 
比較複雜。 下面舉個第二種形式的遞歸調用例子。 
  <4> 遞歸方程爲:T(n) = T(n/3) + T(2n/3) + n 
     爲了更好的理解,先畫出遞歸過程相應的遞歸樹: 
                            n                        --------> n 
                    n/3            2n/3              --------> n 
              n/9       2n/9   2n/9     4n/9         --------> n 
           ......     ......  ......  .......        ...... 
                                                     -------- 
                                                     總共O(nlogn) 
     累計遞歸樹各層的非遞歸項的值,每一層和都等於n,從根到葉的最長路徑是: 
    
      n --> (2/3)n --> (4/9)n --> (12/27)n --> ... --> 1 
     設最長路徑爲k,則應該有: 
      
     (2/3)k次方 * n = 1 
     得到 k = log(2/3)n  // (2/3)爲底 
     於是 T(n) <= (K + 1) * n = n (log(2/3)n + 1) 
     即 T(n) = O(nlogn) 
    由此例子表明,對於第二種遞歸形式調用,藉助於遞歸樹,用迭代法進行算法分析是簡單易行的。





 

遞歸算法的時間複雜度總結

很多算法都使用到了遞歸的方法,或者說遞歸的思想,即把一個問題劃分成同樣的小規模的問題,然後再解決,動態規劃,分而治之都是,遞歸算法的時間複雜度也有規律可循。

針對T(n) = aT(n/b) + f(n)這類遞歸,有以下規定

最後一段說的比較明白,這個定理記不住,但是一些非常典型的例子可以記住:

1.T(n)=2T(n/2)  和 T(n)=2T(n/2) +k

例子有 二叉樹的遞歸遍歷

時間複雜度 T(n)=O(n)

2.T(n)=2T(n/2)+kn+j

例子有 快速排序

時間複雜度爲 T(n)=O(nlogn)

3.T(n)=T(n/2)+kn+j

例子有 二叉樹最近公共祖先

時間複雜度爲T(n)=O(n)




最後對應T(N)=T(N-1)+T(N-2)

運行時間最大是T(N)》(3/2)^N,因此運行時間以指數速度增長。



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