一、算法:算法是解決特定問題求解步驟的描述,在計算機中表現爲指令的有限序列,而且每條指令表示一個或多個操做。git
那麼一個怎樣的算法才能稱得上是好算法,也就是說有沒有什麼標準來評判一個算法的好壞?github
在此以前,我們先來作個試驗:算法
用兩種方式來實現求裴波那契數列第n項的值,一種方式用遞歸方式,第二種方式用普通循環方式。ide
在獲得結果以前,你猜猜那種方式計算結果更快一些,仍是同樣快?函數
測試代碼以下(JavaScript):測試
/** * 一、遞歸實現斐波那契數列: * 1,1,2,3,5... 第n項 = 第n-1項 + 第n-2項 * */ function recursionFbi(n){ if (n < 3) return 1; if (n == 3) return 2; // console.log("遞歸n: ", n); return recursionFbi(n-1) + recursionFbi(n-2); } /** * 二、循環實現裴波那契數列 * 1,1,2,3,5... 第n項 = 第n-1項 + 第n-2項 * */ function circleFbi(n){ if (n < 3) return 1; var a = 1, b = 1, c = 0; for (var i = 0; i < n; i++){ if (i > 1){ c = a + b; a = b; b = c; } console.log("循環i: ", i, ", c: ", c); } return c; }
這裏輸入幾個數字作測試,大體記錄下結果做對比。spa
固然每次測試同一個數字的計算時間不必定都相同,跟當前測試的電腦硬件配置,和其餘應用同開有必定關係。設計
當輸入n=51的時候,測試結果截圖以下:code
還有輸入其餘一些n值的統計數據:blog
上面經過兩種方式求裴波那契數列,表現出來的時間損耗值相差驚人!爲何會有這麼大的差異?這體現了一個好的算法能讓程序的運行效率事半功倍,一個糟糕的算法對於程序來講簡直就是災難!剛纔這兩種算法方式的區別,等下再分析。
二、如今,咱們來講說如何來粗略的估算一個算法的好壞。
咱們對於算法的設計要求是:正確性、可讀性、健壯性、時間效率高、存儲量低。
所以對於一個算法,咱們在運行前,能夠從這五個角度來進行判斷分析,下面主要從時間效率和存儲量角度來細說下:
大O表示法是一種粗略的分析模型,能幫助咱們快速估算一個算法的執行效率,咱們用它來描述算法的時間複雜度。
常見的時間複雜度有這些:
在使用大O推導法時,對於常數階,咱們用常數1代替;有多階項則只保留最高階項;最高階項的常數去除。如圖:
這裏貼上幾個示例用來練習時間複雜度的計算(JavaScript):
//算法複雜度:O(n) function testCount1(n){ //指令計數:多個if算做一個指令 if (n > 10){ console.log("n > 10") } else if(n > 5){ console.log("n > 5") } else{ console.log("n <= 5") } //指令計數:1 + n + n + n = 3n + 1 for (var i = 0; i < n; i++){ console.log("...testCount1...") } } //算法複雜度:O(logn) function testCount3(n){ //指令計數:n/2 = log2(n) //n=2 -> 1; n=4->2; n = 8->3; n = 16->4; n = 32->6 //1=log2(2); 2=log2(4); 3=log2(8); 4=log2(16); 6=log2(32) while((n = n / 2) > 0){ console.log("***testCount3***"); } } //算法複雜度:O(nlogn) function testCount4(n){ //指令計數:1 + 2*log2(n) + log2(n) * (1+3n) = 1 + 3log2(n) + 3n*log2(n) for (var i = 1; i < n; i = i * 2){ //1 + 3n for (var j = 0; j < n; j++){ console.log(">>>testCount4>>>") } } } //算法複雜度:O(n^2) function testCount2(n){ //指令計數:1 + 2n + n * (1+3n) = 1 + 3n + 3n^2 = 3n^2 + 3n + 1 for (var i = 0; i < n; i++){ for (var j = 0; j < n; j++){ console.log("...testCount2...") } } }
常見的時間複雜度所耗時間的大小排列以下:
三、掌握了大O推導法,咱們用大O表示法來論證本文最開始計算裴波那契數列的兩種算法的區別:
3.1 遞歸方式算法,先看下圖:
能夠得出,這裏的遞歸方式算法用大O表示法來表示,它屬於指數階,能夠粗略表示爲:O(n) = 2^n
3.2 而第二種循環方式算法爲線性階的,用大O表示法爲:O(n) = n
3.3 咱們對比一下指數階和線性階的函數曲線圖就知道,n係數越大,這兩種方式的最後的結果相差越大。
因此當n越大時,遞歸方式算法的計算次數呈指數級上升,最後次數結果越大,時間損耗數也就越多。