如何判斷一個算法的好壞 前言 漸進時間複雜度 空間複雜度

前言

小A和小B兩人寫了相同一個功能代碼,而小A的代碼老闆運行後發現耗時爲100ms,消耗內存10MB。而小B的代碼老闆運行以後,發現耗時爲100S,消耗內存100MB。如果你是老闆你會選則使用誰的代碼。對於超過3秒即划走的用戶而言,100s顯然是不行的。小A和小B代碼耗時與運行時佔用內存的2種方式,是判斷算法好壞的最重要的2種標準,分別爲時間複雜度空間複雜度。上面都是程序運行以後才知道耗時與佔用內存,那麼如何在沒有運行程序時對算法進行提前預估呢?

關鍵代碼執行次數

要預估時間複雜度,可以計算算法中關鍵代碼的操作執行次數。

如下

情景一:

小明在繞操場勻速跑步2秒跑完1米,跑300米則需要600秒,如果跑步n米,則需要2 n 秒。則有

T(n) = 2n

情景二:

小明繞操場跑步,跑第1米需要1秒,跑第2米需要2秒,跑第3米需要3秒...跑n米需要n秒。

則小明跑n米的函數計算公式爲

T(n) = 1 + 2 + 3+ 4 + ... + n
= (1 + n)n/2
=0.5n + 0.5n^2

情景三

小明繞操場跑步,總路程40米,第1秒能跑27米,跑第2秒能跑9米,第3秒能跑3米,跑完最後一米需要多長時間。由對數運算公式可得,小明跑完40米的計算公式爲

T(n) = log(3)(40)

若總路程爲n 米,則有

T(n) = log(3)(n)

漸進時間複雜度

通過情景一二的計算,我們可以預估一個算法的時間複雜度,但因爲當n取值不一樣時,仍然不能判斷到底哪一個更快,例如當n爲1時,明顯情景二更快一些。這時我們需要使用漸進時間複雜度進行分析,即大O表示法

當n趨近於無限大時,有 T(n) / f(n) 的極限值有不爲0的常數,則記作T(n) = O(f(n))。

情景一通過大O表示法則爲:O(2n),由於n趨近於無限大,忽略常數項,則記作O(n)
情景二通過大O表示法則爲:O(0.5n + 0.5n^2 ),由於n趨近於無限大,忽略常數項保留最高階項,記作O(n^2)。
情景三通過大O表示法則爲:O(log(3)(n)),由於爲了與冪次方做對比,則通過換底公式有,O(log(2)(n) / log(2)(3)),由於n趨近於無限大忽略除數,底數足夠小省略底數不寫,則有O(log n)
情景四冪函數,同樣通過換底公式則有 O(2^x)

通過下圖,我們分析出當n無限大時,常用的幾個函數耗時從小到大爲:

由於對數函數畫圖時沒找到2爲底在哪設置,hhhh

O(1) < O(log n) < O(n^2) < O(2^n)

空間複雜度

在程序運行指令中,需要存儲一些中間數據所佔用的內存空間即爲空間複雜度,有 S(n) = O(f(n))。

如下函數,傳入的n並不影響i所佔用的空間,記作O(1)

f(n) {
  let i = 3n
}

如下函數,傳入的n所佔用總空間成正比,記作O(n)

f(n) {
  let array = new Array(n)
}

如下函數,傳入的n與二位數組成正比,記作O(n^2)

f(n) {
  let array = new Array(n).fill(new Array(n))
}

取捨

計算機的運行速度和空間資源是有限的,時間複雜度與空間複雜度當然都越小越好。在絕大多數情況下時間複雜度更爲重要一些,我們寧可多分配一些內存空間,也要提升程序的運行速度。

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