遞歸算法的複雜度分析

 首先介紹3種表示複雜度的符號:

  1. Tight 上下界,f = tight(g)表示當輸入大於某個整數時,存在常數c1和c2,使得 c1*g < f < c2*g ;
  2. Lower 下界, f = Lower(g)表示當n足夠大的時候,存在c,使得 f<g *c;
  3. Upper上界, f = upper(g) 表示你足夠大的時候,存在c使得 f> g * c;

算法複雜度分析就是求g的過程。通常來講,算法的上界複雜度纔有意義,即第二種,它表現了算法的優劣。

遞歸算法的複雜度求法不同於普通的算法,以歸併排序爲例,它的複雜度可以表示如下:

T(n) = 2 * T(n/2) +Lower(n)

它表示規模爲n的算法所需時間是規模爲n/2的兩倍加上對整個數組進行掃描的時間。

 

最簡單的求解算法是替換法,它由兩個步驟組成:

  1. 猜測複雜度函數;
  2. 用猜測的函數替換遞歸表達式中的複雜度函數,並推導等式是否成立;

以上面的遞歸表達式爲例,我們猜測複雜度爲g=nlg(n),這也意味着當n最夠大的時候f< n*lg(n)*c

現在用這個表達式替換 T(n/2),並推導出T(n)也符合這個猜測:

T(n)  = 2 * T(n/2) +Lower(n)

       <= 2 * n/2 *lg(n/2)*c+c2 * n

       = n *(lg(n) -1) *c + c2*n

      = n * lg(n)* c + (c2-c)* n

    < c * n * lg(n)

最後一步假設c2<c。由於這個兩個常數都是自己選定的,所以可以做到這一點。

 

在使用替換方法時,最難的是猜測出表達式的形式 g。同時要注意到g中的c是常數,不能隨着n而變化。有時可以通過變量替換的方法,將近似的表達式變換成熟悉的表達式。

 

使用遞歸樹的方法可以較容易的猜測出表達式的形式。遞歸樹的思想是,輸入爲n的複雜度是幾個較小輸入的複雜度的總和,這樣一層一層的劃分下去,會形成一個樹。在樹的每一層會增加複雜度,最後樹的葉子節點是簡單的操作,我們可以認爲他的複雜度爲常數c。這樣我們只需要求的葉子節點的數量,以及樹的層數,就可以計算出整個算法的複雜度。

以上面的歸併排序算法爲例,假設有n個節點,那麼每個父親兩個孩子,所以有lg(n)層,每層複雜度爲n,最後葉節點是n個。所以複雜度是n * lg(n) * c + n, 由於n的次數較低,所以略去。

 

利用遞歸樹可以得到一個遞歸複雜度的通解,如下所示:

0=========(x / V)----+++++(x)+++++-------(x*V)^^^^^^^^^

首先給所有的遞歸複雜度一個統一的定義:

T(n)=aT(n/b)+f(n), a>1,b>1

那麼x表示n的lg(a)/lg(b)次方。

V是n的一個未知的正數次方,任何正數都可以,0.1,0.0001,1,100。

上面那一排表示f(n)可能的複雜度,可以看到從左到右,逐漸增大。隨着f(n)的複雜度不同,算法的複雜度也不一樣。

  1. 在====區域,算法複雜度由x/v確定,T(n)=tight(x)
  2. 在++++區域,複雜度爲 T(n)=Tight(X*lg(n))
  3. 在^^^^區域,複雜度由f(n)確定,T(n)=Tight(f(n))

最後一個有一個額外的限制,不過我還不明白爲什麼需要這個限制,這個限制是:a* f(n/b)<=c*f(n)其中c是小於1的常數。

推理很簡單,設f(n)=n的y次方。

那麼進入第三種情況說明,f(n)>x,也就是說y大於lg(a)/lg(b),也就說明a<b的y次方。

再回到a*f(n/b)<c f(n)上來,如果要滿足題意,只需要則a* (n/b)的y次方<c*n的次方,那麼只需要a<c*b的y次方。

由於a,b,y都是常數,必然可以求得一個c使得上面的等式成立。

 

最後是一個思考題,a[1...n]存放0到n共n+1個整數,只能進行一個操作"讀取第i個元素的第j個bit",如何在Lower(n)的複雜度內找到丟失那個數。

我想到的方法是我們可以知道在0到31個bit上,所有整數共有多少個1是確定的。那麼對n個數掃描32遍,肯定能發現在某幾個位置上1丟失了,把這些1組合起來,就是我們丟失的數組。

不知道更好的解法是什麼。

 

 

 

 

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