算法的評估
對於一個問題,經常有多種不同的求解算法,這時候我們就需要一個對算法進行評估的標准,找出最優的方案,評估一個算法有以下幾個維度:
- 正確性:能正確的實現功能,滿足問題的需求。
- 易讀性:通常,寫出一個利與人類閱讀的代碼和利於機器閱讀的代碼一樣重要
- 健壯性:對於預料之外的輸入,也能做出合適的處理。
- 時空性:算法的時間性能(算法的計算量)和空間性能(算法需要的存儲量)、
時間復雜度
時間復雜度的計算方法
時間復雜度:在給定輸入(問題規模)下,算法的計算量。
所以說,求一個算法的時間復雜度,就是求這個算法在給定問題規模下的計算量,那么問題來了:如何求算法的計算量?
算法計算量的求法規則如下:
- 1、在算法中選擇幾種“基本操作”,例如:賦值語句、數學運算語句等等。
- 2、給定輸入下,計算算法執行了多少次“基本操作”。
- 3、“基本操作”的次數即可作為計算量。
實例與大O表示法
我們以一個例子來說明,求如下表達式的值:
// 階乘的和
1! + 2! + 3! + ... + n!
我們可以寫出如下程序(js代碼):
function factorial (n) { var s = 0, temp = 1 for (var i = 1; i <= n; i++) { temp = 1 for (var j = 1; j <= i; j++) { temp *= j } s += temp } return s }
我們根據之前總結的算法計算量的求法規則可知,求解一個算法的計算量分為三個步驟,第一步:確定基本操作,對於上面的代碼我們所挑選的基本操作如下:
- 第一部分賦值語句:
var s = 0 temp = 1
當我們的輸入規模即 n
變化時,這兩條語句的執行次數沒有變,始終是 2
次。
-
第二部分賦值語句:
for (var i = 1; i <= n; i++) { temp = 1 ... }
第一層循環里的
temp = 1
,該語句的執行次數等於輸入規模n
。 -
乘法計算語句:
for (var j = 1; j <= i; j++) { temp *= j }
第二層循環里的 temp *= j
,該語句的執行次數,當 n = 1
時執行 1 次,當 n = 2
時執行 1 + 2
次,當 n = 3
時執行 1 + 2 + 3
次,所以該語句的執行次數與輸入規模 n
的關系是 1 + 2 + 3 + ... + n = n(n + 1) / 2
。
- 加法計算語句:
第一層循環里的加法賦值語句,該語句的執行次數等於輸入規模for (var i = 1; i <= n; i++) { ... s += temp }
n
。
綜上所述,根據我們選擇的“基本操作”,可以計算出該算法的基本操作次數與輸入規模 n
的關系如下:
T(n) = 2 + n + n(n + 1) / 2 + n = 1/2n^2 + 3/2n + 2
當 n
足夠大時,n^2
起支配作用,使用 O(n^2)
表示 T(n)
的近似值,這種表示法成為 大 O 表示法
。
常見的時間復雜度階數
- 常熟階 O(1):即算法的計算量不隨着輸入規模的變化而變化。
- 線性階 O(n)
- 多項式階 O(n^c):常見的多項式階如 O(n^2)、O(n^3)
- 指數階 O(C^n):常見的指數階如 O(2^n)
一般我們認為一個算法的時間復雜度為指數階的時候,該算法是實際不可運算的,大家可以想象一下,如果一個算法的時間復雜度為 O(2^n)
當 n = 1000
時,這個數值是何等恐怖。更何況我們的輸入規模 n
很可能遠大於 1000。
另外我們認為時間復雜度為 O(n)
、O(log2N)
、O(n^2)
是高效的算法。對於合理大的 n
,O(n^3)
也是可以接受的。
空間復雜度
空間復雜度的計算方法
空間復雜度:給定輸入(問題規模)下,算法運行時所占用的臨時存儲空間。
一個算法執行期間所占用的存儲量分為三部分:
- 算法本身的代碼所占用的空間
- 輸入數據所占用的空間
- 輔助變量所占用的空間
由於實現不同算法所需的代碼不會有數量級的差別,所以算法本身代碼所占用的空間我們可以不考慮
輸入的數據所占用的空間是由問題決定的,與算法無關,所以我們也不需要考慮
我們需要考慮的只有一個:程序執行期間,輔助變量所占用的空間。
計算方法類似於計算算法的時間復雜度,空間復雜度我們用 S(n)
來表示,它同樣是輸入數據規模 n
的函數,用大 O 表示法記為:
S(n) = O(g(n))
其中 g(n)
是一個關於 n
的函數,如:g(n) = n
、g(n) = n^2
、g(n) = log2N
等等。
實例
假設我們有一個數組,該數組有100個元素,寫一個轉置該數組的算法:
function reverse (arr) { for (var i = 0; i <= arr.length / 2 - 1; i++) { var temp = arr[i] arr[i] = arr[arr.length - i - 1] arr[arr.length - i - 1] = temp } }
上面的算法中,我們采用中間變量 temp
對數組的值進行逐個對應的首尾交換,最終達到轉置的目的,我們可以看到,輔助變量只有一個即 temp
,該變量存儲一個數字類型的值,temp
所占用的內存不會隨輸入數組規模的增大而增大,所以上面算法的控件復雜度為 O(1)
,是常數階,即上面算法的空間復雜度 S(n) = O(1)
。
from: http://blog.poetries.top/js-knowledge-note/#/note/algorithm/time-space