算法之複雜度判斷

一、算法:算法是解決特定問題求解步驟的描述,在計算機中表現爲指令的有限序列,而且每條指令表示一個或多個操做。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;
}
View Code

這裏輸入幾個數字作測試,大體記錄下結果做對比。spa

固然每次測試同一個數字的計算時間不必定都相同,跟當前測試的電腦硬件配置,和其餘應用同開有必定關係。設計

當輸入n=51的時候,測試結果截圖以下:code

還有輸入其餘一些n值的統計數據:blog

   上面經過兩種方式求裴波那契數列,表現出來的時間損耗值相差驚人!爲何會有這麼大的差異?這體現了一個好的算法能讓程序的運行效率事半功倍,一個糟糕的算法對於程序來講簡直就是災難!剛纔這兩種算法方式的區別,等下再分析。

 

二、如今,咱們來講說如何來粗略的估算一個算法的好壞。

  咱們對於算法的設計要求是:正確性、可讀性、健壯性、時間效率高、存儲量低

  所以對於一個算法,咱們在運行前,能夠從這五個角度來進行判斷分析,下面主要從時間效率和存儲量角度來細說下:

  • 時間複雜度(time complexity):估算程序指令的執行次數(執行時間)
  • 控件複雜度(space complexity):估算所需佔用的存儲空間

  

  大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...")
        }
    }
}
View Code

 常見的時間複雜度所耗時間的大小排列以下:

 

三、掌握了大O推導法,咱們用大O表示法來論證本文最開始計算裴波那契數列的兩種算法的區別:

 3.1 遞歸方式算法,先看下圖:

  

  能夠得出,這裏的遞歸方式算法用大O表示法來表示,它屬於指數階,能夠粗略表示爲:O(n) = 2^n

  3.2 而第二種循環方式算法爲線性階的,用大O表示法爲:O(n) = n

  3.3 咱們對比一下指數階和線性階的函數曲線圖就知道,n係數越大,這兩種方式的最後的結果相差越大。

    因此當n越大時,遞歸方式算法的計算次數呈指數級上升,最後次數結果越大,時間損耗數也就越多。

 

測試Demo地址:https://github.com/xiaotanit/Tan_DataStruct

相關文章
相關標籤/搜索