數據結構和算法(Golang實現)(9)基礎知識-算法複雜度及漸進符號

算法複雜度及漸進符號

1、算法複雜度

首先每一個程序運行過程當中,都要佔用必定的計算機資源,好比內存,磁盤等,這些是空間,計算過程當中須要判斷,循環執行某些邏輯,周而反覆,這些是時間。算法

那麼一個算法有多好,多快,怎麼衡量一個算法的好壞?因此,計算機科學在算法分析過程當中,提出了算法複雜度理論,這套理論能夠量化算法的效率,以此做爲標準,方便咱們能衡量到底選擇哪種算法。segmentfault

複雜度有兩個維度:時間和空間。數組

咱們說,一個實現了某算法的程序:數據結構

  1. 若是計算的速度越快,那麼這個算法時間複雜度越低。
  2. 若是佔用的計算資源越少,那麼空間複雜度越低。

咱們要選擇複雜度低的算法,衡量好空間和時間的消耗,選出適合特定場景的算法。併發

這兩個複雜度維度的量化過程都是同樣的,因此咱們這裏主要介紹時間複雜度。數據結構和算法

2、算法規模

咱們要計算公式1 + 2 + 3 + ... + 100,那麼按照最直觀的算法來寫:函數

package main

import "fmt"

func sum(n int) int {
    total := 0
    // 從1加到N, 1+2+3+4+5+..+N
    for i := 1; i <= n; i++ {
        total = total + i
    }
    return total
}

func main() {
    fmt.Println(sum(100))
}

n = 10時就等於咱們要計算的公式。這個算法要循環n-1次,當n很小時,計算很快,但當n無限大的時候,計算很慢。spa

因此,算法衡量要衡量的是在不一樣問題規模 n下,算法的速度。code

在這裏,由於要循環計算n-1次,而當n無限大時,常數項基本忽略不計,因此這個算法的時間複雜度,咱們用O(n)來表示。協程

咱們有另一種計算方式:

func sum2(n int) int {
    total := ((1 + n) * n) / 2
    return total
}

此次算法只需執行1次,因此這個算法的時間複雜度是O(1)。能夠看出,時間複雜度爲O(1)的算法優於複雜度爲O(n)的算法。

固然,還有指數級別的好比以前的漢諾塔算法,對數級別的,階乘級別的複雜度,如O(2^n)O(n!)O(logn)等。

算法的優先級排列以下,通常排在上面的要優於排在下面的:

  1. 常數複雜度:O(1)
  2. 對數複雜度:O(logn)
  3. 一次方複雜度:O(n)
  4. 一次方乘對數複雜度:O(nlogn)
  5. 乘方複雜度:O(n^2)O(n^3)
  6. 指數複雜度:O(2^n)
  7. 階乘複雜度:O(n!)
  8. 無限大指數複雜度:O(n^n)

3、漸進符號

如何量化一個複雜度,到底有多複雜,計算機科學抽象出了幾個複雜度漸進符號。

漸進符號以下:

OοΘΩω

分別讀做:Omicron(大歐),omicron(小歐),Theta(西塔),Omega(大歐米伽),omega(小歐米伽)。

3.1. 漸進符號:Θ

假設算法A的運行時間表達式:

T(n)= 5 * n^3 + 4 * n^2

若是問題規模n足夠大,那麼低次方的項將無足輕重,運行時間主要取決於高次方的第一項:5*n^3

隨着n的增大,第一項的5*n^3中的常數5也無足輕重了。

因此算法A的運行時間T(n)約等於n^3。記爲:

T(n) = Θ(n^3)

Θ的數學含義:

f(n)g(n)是定義域 n爲天然數集合的函數,兩個函數同階,也就是當 n無窮大時, f(n)/g(n)等於某個大於0的常數 c
也能夠說,存在正常量 c1c2n0,對於全部 n >= n0,有 0 <= c1 * g(n) <= f(n) <= c2 * g(n)
那麼能夠記 f(n) = Θ(g(n))g(n)f(n)的漸進緊確界。

3.2. 漸進符號:O

O的數學含義:

f(n)g(n)是定義域 n爲天然數集合的函數, f(n)函數的階不高於 g(n)函數的階。
也能夠說,存在正常量 cn0,對於全部 n >= n0,有 0 <= f(n) <= c * g(n)
那麼能夠記 f(n) = O(g(n))g(n)f(n)的漸進上界。

3.3. 漸進符號:Ω

Ω的數學含義:

f(n)g(n)是定義域 n爲天然數集合的函數, f(n)函數的階不低於 g(n)函數的階。
也能夠說,存在正常量 cn0,對於全部 n >= n0,有 0 <= cg(n) <= f(n)
那麼能夠記 f(n) = Ω(g(n))g(n)f(n)的漸進下界。

3.4. 漸進分析

上面的定義很複雜,咱們能夠來看圖:

n值超過某個值時,f(n)g(n)兩條線夾在中間,那麼g(n)就是漸進緊確界。

若是g(n)的線在上面,就是漸進上界。

若是g(n)線在下面,就是漸進下界。

咱們通常會評估一個算法的漸進上界O,由於這表示算法的最壞狀況,這個上界能夠十分不許確,但咱們通常會評估得足夠準確,好比:

設 f(n) = 5 * n^3 + 4 * n^2,咱們要求漸進上界。

那麼:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4

兩個g(n)都是上界,由於令c = 5時都存在:0 <= f(n) <= c * g(n))

咱們會取乘方更小的那個,由於這個界更逼近f(n)自己,因此咱們通常說f(n) = O(n^3),算法的複雜度爲大歐n的三次方,表示最壞狀況。

同理,漸進下界Ω恰好與漸進上界相反,表示最好狀況。好比仍是這個假設:

設 f(n) = 5 * n^3 + 4 * n^2,咱們要求漸進下界。

那麼:

f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

兩個g(n)都是下界,由於令c =5時都存在:0 <= cg(n) <= f(n)

咱們準確評估的時候,要取乘方更大的那個,由於這個界更逼近f(n)自己,因此咱們通常說f(n) = Ω(n^3),算法的複雜度爲大歐米伽n的三次方,表示最好狀況。

咱們發現當f(n) = Ω(n^3) = O(n^3)時,其實f(n) = Θ(n)

另外兩個漸進符號οω通常不多使用,指不那麼緊密的上下界。

也就是評估的時候,不那麼準確去評估,在評估最壞狀況的時候使勁地往壞了評估,評估最好狀況則使勁往好的評估,可是它不能剛恰好,好比上面的結果:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4
f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

咱們能夠說:

f(n) = ο(n^4),g(n) = n^4  往高階的評估,不能同階
f(n) = ω(n^2),g(n) = n^2  往低階的評估,不能同階

4、總結

咱們通常用O漸進上界來評估一個算法的時間複雜度,表示逼近的最壞狀況。其餘漸進符合基本不怎麼使用。

系列文章入口

我是陳星星,歡迎閱讀我親自寫的 數據結構和算法(Golang實現),文章首發於 閱讀更友好的GitBook

相關文章
相關標籤/搜索