1. 程式人生 > >演算法分析——排序演算法(歸併排序)複雜度分析(主定理法)

演算法分析——排序演算法(歸併排序)複雜度分析(主定理法)

       前兩篇文章中分別是要用遞迴樹代換法對歸併排序的時間複雜度進行了簡單的分析和證明,經過兩次分析後,我們發現遞迴樹法的特點是:可以很直觀的反映出整個歸併排序演算法的各個過程,但因為要畫出遞迴樹所以比較麻煩,所以遞迴樹演算法更適合新手,因為它可以讓分析者更直觀、簡易的理解遞迴式演算法的各個過程並對各個過程的分析、合併,但因為需要要畫遞迴樹,所以比較複雜代換法也僅適合在已經大致猜測出演算法的複雜度的時候,對猜測解進行證明。那有沒有一種分析方法即可以在沒有事先明確演算法複雜度的大致解,又可以讓分析者在較短的時間算出演算法的複雜度?

數學定義

        今天這篇文章講述的主定理法就可以通過套用公式簡單、快速算出遞迴式演算法的複雜度。下面我們首先描述下主定理法的數學定義:

         遞迴式T(n) = aT(\frac{n}{b}) + f(n),函式T(n)需要滿足設a\geq 1,b> 1b > 1f(n)為正函式,\frac{n}{b}可能是\frac{n}{b}\left \lfloor \frac{n}{b} \right \rfloor\left \lceil \frac{n}{b} \right \rceil遞迴式函式T(n)可能的漸進界有以下三種可能:

          1.若存在某常數\varepsilon > 0,都有f(n) = O(n^{({log_{b}}a)-\varepsilon }),則T(n) = \theta (n^{^{{log{b}}^{a}}})     

          2.若f(n) = \theta (n^{{log_{b}}^{a}}),則T(n) = \theta (n^{{log_{b}}{a}}logn) 

          3.若存在某常數\epsilon > 0,有f(n)=Ω(n^{{log_{b}}^{a}+\varepsilon })f(n) = \Omega (n^{({log_{b}}{a})+\varepsilon }),且存在某常數c< 1與所有足夠大的n,有af(\frac{n}{b}) \leq cf(n),則T(n) = \Theta (f(n))

瞭解了主定理的數學定義之後,我們只需要將歸併排序的遞迴式代入到主定理的數學公式中,即可在很短的時間內獲得歸併排序的時間複雜度,下面就會給出證明過程。

歸併排序時間複雜度求解

對於歸併排序的時間複雜度函式T(n) = 2T(\frac{n}{2}) + O(n),可知a=2,b=2,f(n) = O(n),所以符合第二種情況,由此可得T(n) = \Theta (nlogn)。  

      看到這裡,大家是不是覺得主定理法在解歸併排序或者說在解遞迴演算法的複雜度函式式時,特別的方便和快捷。但現在應該還會有很多人在疑惑主定理這麼好用,但這個公式是怎麼的來的?在任何情況下的遞迴式都可以使用嗎?怎麼證明這個公式是正確的?

主定理公式證明

        由上述的數學定義中可知,b在取值時可分為三種情況,即第一種n可被b整除時取\frac{n}{b},第二種n不可被b整除時取\left \lfloor \frac{n}{b} \right \rfloor,第三種n不可被b整除時取\left \lceil \frac{n}{b} \right \rceil。這裡首先使用n可被b整除的情況進行證明。在進行公式證明之前,這裡做個簡單而且更直觀的圖形化解讀,這樣可以讓一些基礎比較差的同學更好的理解主定理的數學定義。

由下圖我們可以得知,遞迴樹的根節點為f(n),隨後每往下一層都會分裂為a個問題規模為f(\frac{n}{b})的子節點,分裂{log_{b}}{a}次後分裂結束,此時分裂出n^{{log_{b}}{a}}葉子節點,葉子節點的問題規模為\Theta (1)。由遞迴樹可得 

               T(n) = \sum_{j=0}^{({log_{b}}{n})-1}a^{j}f(\frac{n}{b^{j}}) + \Theta (n^{{log_{b}}{a}})

證明1.若存在某常數\varepsilon > 0,都有f(n) = O(n^{({log_{b}}a)-\varepsilon }),則T(n) = \theta (n^{^{{log{b}}^{a}}})     

             f(\frac{n}{b^{j}}) = O((\frac{n}{b^{j}})^{({log_{b}}{a})-\varepsilon}) = c(\frac{n}{b^{j}})^{({log_{b}}{a})-\varepsilon}

             T(n) = \sum_{j=0}^{({log_{b}}{n})-1}a^{j}f(\frac{n}{b^{j}}) + \Theta (n^{{log_{b}}{a}}) \leq \sum_{j=0}^{({log_{b}}{n})-1}c.a^{j} (\frac{n}{b^{j}})^{({log_{b}}{a})-\varepsilon} + \Theta (n^{{log_{b}}{a}})

             T(n) = cn^{({log_{b}}{a}) - \varepsilon } \sum_{j=0}^{({log_{b}}{n})-1}\frac{a^{j}}{(b^{({log_{b}}{a})-\varepsilon })^{j}}+ \Theta (n^{{log_{b}}{a}})

             T(n) =cn^{({log_{b}}{a}) - \varepsilon } \sum_{j=0}^{({log_{b}}{n})-1}\frac{a^{j}}{(\frac{a}{b^{\varepsilon }})^{j}} + \Theta (n^{{log_{b}}{a}})

             T(n)= cn^{({log_{b}}{a}) - \varepsilon } \sum_{j=0}^{({log_{b}}{n})-1}(b^{\varepsilon })^{j} + \Theta (n^{{log_{b}}{a}}) 

這時可以看出\sum_{j=0}^{({log_{b}}{n})-1}(b^{\varepsilon })^{j}其實是個等比序列,等比序列求和公式S(n) = a1.\frac{1-q^{n}}{1-q}a1為序列首項,q為公比,所以可得

              T(n)= cn^{({log_{b}}{a}) - \varepsilon }.\frac{1-(b^{\varepsilon })^{{log_{b}}{n}}}{1-b^{\varepsilon }} + \Theta (n^{{log_{b}}{a}})

              T(n) = cn^{({log_{b}}{a}) - \varepsilon }.\frac{1-n^{\varepsilon }}{1-b^{\varepsilon }}+ \Theta (n^{{log_{b}}{a}}) = cn^{({log_{b}}{a}) - \varepsilon }\frac{n^{\varepsilon }-1}{b^{\varepsilon }-1}+ c1. n^{{log_{b}}{a}}  

              T(n) =\frac{1}{b^{\varepsilon }-1}(cn^{({log_{b}}{a})-\varepsilon }n^{\varepsilon }-cn^{({log_{b}}{a})-\varepsilon })+ c1. n^{{log_{b}}{a}}

              T(n) = \frac{1}{b^{\varepsilon }-1}(cn^{{log_{b}}{a}}-cn^{({log_{b}}{a})-\varepsilon }) + c1. n^{{log_{b}}{a}}

因為\varepsilon > 0,所以T(n)的最高階則為n^{{log_{b}}{a}},所以T(n) = \theta (n^{^{{log{b}}^{a}}})情況1證畢。

證明2.若f(n) = \theta (n^{{log_{b}}^{a}}),則T(n) = \theta (n^{{log_{b}}{a}}logn) 

              f(\frac{n}{b^{j}}) = \Theta ((\frac{n}{b^{j}})^{{log_{b}}{a}})

              T(n) = \sum_{j=0}^{({log_{b}}{n})-1}a^{j}f(\frac{n}{b^{j}}) + \Theta (n^{{log_{b}}{a}}) 

              T(n) = \Theta( \sum_{j=0}^{({log_{b}}{n})-1}a^{j}((\frac{n}{b^{j}})^{{log_{b}}{a}})) + \Theta (n^{{log_{b}}{a}})

              T(n) = \Theta( n^{{log_{b}}{a}}\sum_{j=0}^{({log_{b}}{n})-1}\frac{a^{j}}{a^{j}}) + \Theta (n^{{log_{b}}{a}})

根據公式可得 T(n) = \Theta ({n^{{log_{b}}{a}}log_{b}}{n}),再根據對數函式的換地公式最後可得T(n) = \Theta ({n^{{log_{b}}{a}}log_}{n})情況2證畢。

證明3.若存在某常數\epsilon > 0,有f(n) = \Omega (n^{({log_{b}}{a})+\varepsilon }),且存在某常數c< 1與所有足夠大的n,有af(\frac{n}{b}) \leq cf(n),則T(n) = \Theta (f(n))

               T(n) = \sum_{j=0}^{({log_{b}}{n})-1}a^{j}f(\frac{n}{b^{j}}) + \Theta (n^{{log_{b}}{a}})               

               \because af(\frac{n}{b}) \leq cf(n)

                \therefore a^{j}f(\frac{n}{b^{j}}) \leq c^{j}f(n)

               T(n) \leq \sum_{j=0}^{({log_{b}}{n})-1}c^{j}f(n) + \Theta (n^{{log_{b}}{a}}) = f(n)\sum_{j=0}^{({log_{b}}{n})-1}c^{j} + \Theta (n^{{log_{b}}{a}})

              T(n) \leq f(n) \sum_{j=0}^{\propto }c^{j} + \Theta (n^{{log_{b}}{a}})

              \because c<1 \therefore T(n) \leq \frac{1}{1-c}f(n) + \Theta (n^{{log_{b}}{a}})

              \because f(n) = \Omega (n^{({log_{b}}{a})+\varepsilon }) 所以f(n)的最高階大於\Theta (n^{{log_{b}}{a}})的最高階

               \therefore T(n) = O(f(n))     

               T(n) = \sum_{j=0}^{({log_{b}}{n})-1}a^{j}f(\frac{n}{b^{j}}) + \Theta (n^{{log_{b}}{a}})

               T(n) = f(n) + af(\frac{n}{b}) + ...+ a^{{log_{b}}{a}}f(\frac{n}{b^{{log_{b}}{a}}}) + \Theta (n^{{log_{b}}{a}})   

               T(n) = \Omega (f(n))+ \Theta (n^{{log_{b}}{a}})= \Omega (f(n))  

T(n) = O(f(n))和 T(n) = \Omega (f(n))  可得  T(n) =\Theta (f(n)) 。情況3證畢。

        至此,在n可以被b整除的情況已經證明完畢,因為篇幅問題,這裡不在對n不可以被b整除的另外兩種情況證明,有興趣的同學可以網上搜索一下,資料很多。經過了詳細的數學公式證明後,相信很多人已經瞭解了主定理的證明。但此時應該還有很多人疑惑,為什麼主定理分為了三種情況,這三種情況又是依據什麼進行劃分的?數學基礎比較好的同學現在應該已經理解,這裡將做一個簡單的文字闡述,以便數學基礎較差的同學理解。

       由上圖的遞迴樹可得知,整個遞迴樹的複雜度是由遞迴樹中各層節點的加和所得。而每層節點的複雜度也是呈數學級數改變。而主定理的三種情況就是根據每層節點複雜度遞增或遞減時的數學級數的不同所制定。

        第一種情況:節點的複雜度隨著遞迴樹的深度越深而呈幾何級數(甚至更高)單調遞增,所以在遞迴樹的葉子節點的複雜度的階數是最高的,也就是說遞迴式的複雜度由葉子節點複雜度的階數所決定,所以T(n) = \theta (n^{^{{log{b}}^{a}}})

        第三種情況:節點的複雜度隨著遞迴樹的深度越深而呈幾何級數(甚至更高)單調遞減,所以在遞迴樹的根節點的複雜度的階數是最高的,也就是說遞迴式的複雜度由葉子節點複雜度的階數所決定,所以T(n) = \Theta (f(n))

第二種情況:節點的複雜度隨著遞迴樹的深度越深而呈較小級數(甚至不變)單調遞減或遞減,所以在遞迴樹的各個節點的複雜度的階數相似,也就是說遞迴式的複雜度由所有子節點複雜度的階數所決定,而由於每層節點的複雜度變動較小(甚至不變),所以在漸進角度看,可以認為每層所有節點的複雜度之和大致相同為\theta (n^{^{{log{b}}^{a}}}),所以T(n) = \theta (n^{{log_{b}}{a}}logn)

註釋①:得到該公式的過程,此處不在詳述,大致意思為遞迴樹中每往下一層,子節點的複雜度便會降低父節點的複雜度的c倍(c< 0),此處可以也寫為a^{j}f(\frac{n}{b^{j}}) \leq cf(n),不過這樣的寫法並不是漸進緊確的。