轉:算法時間複雜度基礎

摘要
      本文論述了在算法分析領域一個重要問題——時間複雜度分析的基礎內容。本文將首先明確時間複雜度的意義,然後以形式化方式論述其在數學上的定義及相關推導。從而幫助你們從本質上認清這個概念。

前言
      一般,對於一個給定的算法,咱們要作 兩項分析。第一是從數學上證實算法的正確性,這一步主要用到形式化證實的方法及相關推理模式,如循環不變式、數學概括法等。而在證實算法是正確的基礎上,第二部就是分析算法的時間複雜度。算法的時間複雜度反映了程序執行時間隨輸入規模增加而增加的量級,在很大程度上能很好反映出算法的優劣與否。所以,做爲程序員,掌握基本的算法時間複雜度分析方法是頗有必要的。
      可是不少朋友並不能清晰的理解這一律念,究其緣由,主要是由於沒有從數學層面上理解其本質,而是習慣於從直觀理解。下面,咱們就一步步走近算法時間複雜度的數學本質。

算法時間複雜度的數學意義
      從數學上定義,給定算法A,若是存在函數F(n),當n=k時,F(k)表示算法A在輸入規模爲k的狀況下的運行時間,則稱F(n)爲算法A的時間複雜度
      這裏咱們首先要明確輸入規模的概念。關於輸入規模,不是很好下定義,非嚴格的講,輸入規模是指算法A所接受輸入的天然獨立體的大小。例如,對於排序算法來講,輸入規模通常就是待排序元素的個數,而對於求兩個同型方陣乘積的算法,輸入規模能夠看做是單個方陣的維數。爲了簡單起見,在下面的討論中,咱們老是假設算法的輸入規模是用大於零的整數表示的,即n=1,2,3,……,k,……
      咱們還知道,對於同一個算法,每次執行的時間不只取決於輸入規模,還取決於輸入的特性和具體的硬件環境在某次執行時的狀態。因此想要獲得一個統一精確的F(n)是不可能的。爲了解決這個問題,咱們作一下兩個說明:
      1.忽略硬件及環境因素,假設每次執行時硬件條件和環境條件是徹底一致的。
      2.對於輸入特性的差別,咱們將從數學上進行精確分析並帶入函數解析式。

算法時間複雜度分析示例
      爲了便於朋友們理解,我將不會採用教科書上慣用的快速排序、合併排序等經典示例進行分析,而是使用一個十分簡單的算法做爲示例。咱們先來定義問題。
      問題定義:
      輸入——此問題輸入爲一個有序序列,其元素個數爲n,n爲大於零的整數。序列中的元素爲從1到n這n個整數,但其順序爲徹底隨機。
      輸出——元素n所在的位置。(第一個元素位置爲1)

      這個問題很是簡單,下面直接給出其解決算法之一(僞代碼):

      LocationN(A)
      {
            for(int i=1;i<=n;i++)-----------------------t1
            {
                  if(A[i] == n) ----------------------------t2
                        { return i; }------------------------t3
            }
      }

      咱們來看看這個算法。其中t一、t2和t3分別表示此行代碼執行一次須要的時間。
      首先,輸入規模n是影響算法執行時間的因素之一。在n固定的狀況下,不一樣的輸入序列也會影響其執行時間。最好狀況下,n就排在序列的第一個位置,那麼此時的運行時間爲「t1+t2+t3」。最壞狀況下,n排在序列最後一位,則運行時間爲「n*t1+n*t2+t3=(t1+t2)*n+t3」。能夠看到,最好狀況下運行時間是一個常數,而最壞狀況下運行時間是輸入規模的線性函數。那麼,平均狀況如何呢?
      問題定義說輸入序列徹底隨機,即n出如今1...n這n個位置上是等可能的,即機率均爲1/n。而平均狀況下的執行次數即爲執行次數的數學指望,其解爲:

      E
      = p(n=1)*1+p(n=2)*2+...+p(n=n)*n
      = (1/n)*(1+2+...+n)
      = (1/n)*((n/2)*(1+n))
      = (n+1)/2

      即在平均狀況下for循環要執行(n+1)/2次,則平均運行時間爲「(t1+t2)*(n+1)/2+t3」。
      由此咱們得出分析結論:
      t1+t2+t3 <= F(n) <= (t1+t2)*n+t3,在平均狀況下F(n) = (t1+t2)*(n+1)/2+t3

算法的漸近時間複雜度
      以上分析,咱們對算法的時間複雜度F(n)進行了精確分析。可是,不少時候,咱們不須要進行如此精確的分析,緣由有下:
      1.在較複雜的算法中,進行精確分析是很是複雜的。
      2.實際上,大多數時候咱們並不關心F(n)的精確度量,而只是關心其量級。
      基於此,提出漸近時間複雜度的概念。在正式給出漸近時間複雜度以前,要先給出幾個數學定義:

      定義一:Θ(g(n))={f(n) | 若是存在正常數c一、c2和正整數n0,使得當n>=n0時,0<c1g(n)<=f(n)<=c2g(n)恆成立}html

  定義二:O(g(n))={f(n) | 對任意正常數c,存在正整數n0,使得當n>=n0時,0<=f(n)<=cg(n)恆成立}
      定義三:Ω(g(n))={f(n) | 若是存在正常數c和正整數n0,使得當n>=n0時,0<=cg(n)<=f(n)恆成立}

      能夠看到,三個定義其實都定義了一個函數集合,只不過集合中的函數須要知足的條件不一樣。有了以上定義,就能夠定義漸近時間複雜度了。
      不過這裏還有個問題:F(n)不是肯定的,他是在一個範圍內變更的,那麼咱們關心哪一個F(n)呢?通常咱們在分析算法時,使用最壞狀況下的F(n)來評價算法效率,緣由有以下兩點:
      1.若是知道了最壞狀況,咱們就能夠保證算法在任什麼時候候都不能比這個狀況更壞了。
      2.不少時候,算法運行發生最壞狀況的機率仍是很大的,如查找問題中待查元素不存在的狀況。且在不少時候,平均狀況的漸近時間複雜度和最壞狀況的漸近時間複雜度是一個量級的。

      因而給出以下定義:設F(n)爲算法A在最壞狀況下F(n),則若是F(n)屬於Θ(g(n)),則說算法A的漸近時間複雜度爲g(n),且g(n)爲F(n)的漸近確界

      仍是以上面的例子爲例,則在上面定義中F(n) = (t1+t2)*n+t3。則F(n)的漸近確界爲n,其證實以下:

      證實:
      設c1=t1+t2,c2=t1+t2+t3,n0=2
      又由於 t1,t2,t3均大於0
      則,當n>n0時,0<c1n<=F(n)<=c2n 即 0<(t1+t2)*n<=(t1+t2)*n+t3<=(t1+t2+t3)*n恆成立。
      因此 F(n)屬於Θ(n)
      因此 n是F(n)的漸近確界
      證畢

      在實際應用中,咱們通常都是使用漸近時間複雜度代替實際時間複雜度來進行算法效率分析。通常認爲,一個漸近複雜度爲n的算法要優於漸近複雜度爲n^2的算法。注意,這並非說漸近複雜度爲n的算法在任何狀況下都必定更高效,而是說在輸入規模足夠大後(大於臨界條件n0),則前一個算法的最壞狀況老是好於後一個算法的最壞狀況。事實證實,在實踐中這種分析是合理且有效的。
      相似的,還能夠給出算法時間複雜度的上確界和下确界 
      設F(n)爲算法A在最壞狀況下F(n),則若是F(n)屬於Ο(g(n)),則說算法A的漸近時間複雜度上限爲g(n),且g(n)爲F(n)的漸近上確界。
      設F(n)爲算法A在最壞狀況下F(n),則若是F(n)屬於Ω(g(n)),則說算法A的漸近時間複雜度下限爲g(n),且g(n)爲F(n)的漸近下确界。

      這裏必定要注意,因爲咱們是以F(n)最壞狀況分析的,因此,咱們能夠100%保證在輸入規模超過臨界條件n0時,算法的運行時間必定不會高於漸近上確界,可是並不能100%保證算法運行時間不會低於漸近下确界,而只能100%保證算法的最壞運行時間不會低於漸近下确界。

總結
      算法時間複雜度分析是一個很重要的問題,任何一個程序員都應該熟練掌握其概念和基本方法,並且要善於從數學層面上探尋其本質,才能準確理解其內涵。在以上分析中,咱們只討論了「緊確界」,其實在實際中漸近確界還分爲「緊確界」和「非緊確界」,有興趣的朋友能夠查閱相關資料。
      好了,本文就到這裏了,但願本文內容能對各位有所幫助。程序員

 

轉載地址:http://www.cnblogs.com/leoo2sk/archive/2008/11/14/1332381.html算法

相關文章
相關標籤/搜索