演算法的時間複雜度

call_me_R發表於2019-01-08

前言

作為一個非典型的前端開發人員,我們要懂得一些演算法的概念,並將其理論知識引入日常的開發中,提高日常的開發效率和提升產品的體驗。

本篇博文的概念偏多,模糊的點,有興趣的谷歌起來啦!

相關概念

演算法: 演算法是指解題方案的準確而完整的描述,是一系列解決問腿的清晰指令,演算法代表著用系統的方法描述解決問題的策略機制。

演算法的效率: 是指演算法執行的時間,演算法執行時間需要通過演算法編制的程式在計算機上執行時所消耗的時間來衡量。

一個演算法的優劣可以用空間複雜度時間複雜度來衡量。

時間複雜度:評估執行程式所需的時間。可以估算出程式對處理器的使用程度。

空間複雜度:評估執行程式所需的儲存空間。可以估算出程式對計算機記憶體的使用程度。

演算法設計時,時間複雜要比空間複雜度更容易複雜,所以本博文也在標題指明討論的是時間複雜度。一般情況下,沒有特殊說明,複雜度就是指時間複雜度

時間頻度: 一個演算法中的語句執行次數稱為語句頻度或時間頻度。

一個演算法執行所消耗的時間,從理論上是不能算出來的,必須上機測試才知道。但我們不可能也沒有必要對每個演算法都上機測試,只需要知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且一個演算法花費的時間與演算法中語句執行次數成正比例,哪個演算法中執行語句次數多,它話費的時間就多。

時間複雜度: 執行程式所需的時間。(上面提到了)

一般情況下,演算法中基本操作重複執行的次數是問題規模n的某個函式,用T(n)表示,若有某個輔助函式f(n),使得當n趨近無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱為f(n)是T(n)的同數量級函式。記作T(n)=O(f(n)),稱O(f(n))為演算法的漸進時間複雜度,簡稱時間複雜度。比如:

在 T(n)=4nn-2n+2 中,就有f(n)=nn,使得T(n)/f(n)的極限值為4,那麼O(f(n)),也就是時間複雜度為O(n*n)

大O表示法: 演算法的時間複雜度通常用大O符號表述,定義為T(n)=Of((n))【上面有提到並舉例】。

T(n) = O(f(n))稱函式T(n)以f(n)為界或稱T(n)受限於f(n)。如果一個問題的規模是n,解決一問題的某一演算法所需要的時間為T(n)。

【注】時間複雜度和時間複雜度雖然在概念上有所區別,但是在某種情況下,可以認為兩者是等價的或者是約等價的。

大O階推導

推導大O階就是將演算法的所有步驟轉換為代數項,然後排除不會對問題的整體複雜度產生較大影響的較低階常數和係數。

有條理的說,推導大O階,按照下面的三個規則來推導,得到的結果就是大O表示法:

  1. 執行時間中所有的加減法常數用常數1代替
  2. 只保留最高階項
  3. 去除最高項常數

先來看下圖,對各個時間複雜度認下臉:

comparision_computational_cmplexity

O(1)常數階

let sum = 0,
    n = 100; // 執行一次
sum = (1+n)*n/2; // 執行一次
console.log(sum); // 執行一次 
複製程式碼

上面演算法的執行次數的函式是f(n)=3,則有O(f(n) = 3)即O(3), 常數項用常數1表示,則最終的表示法為O(1),我們稱之為常數階。

O(n)線性階

線性階主要分析迴圈結構的執行情況,如下:

for(let i = 0; i < n; i++){
    // 時間複雜度O(1)的演算法
    ...
}
複製程式碼

上面演算法迴圈體中的程式碼執行了n次,因此時間複雜度是O(n)

O(logn)對數階

let number = 1;
while(number < n){
    number = number*2;
    // 時間複雜度O(1)的演算法
    ...
}
複製程式碼

上面的程式碼,隨著number每次乘以2後,都會越來約接近n,當number不小於n時候就會退出迴圈。假設迴圈的次數為x,則由2^x=n得出x=log₂n,因此得到這個演算法的時間複雜度為O(logn)

O(n²)平方階

平凡階一般出現在巢狀的迴圈中,如下:

for(let i=0; i<n; i++){
    for(let j=i; j<n; j++){
        // 時間複雜度O(1)的演算法
        ...
    }
}
複製程式碼

上面的程式碼中,內迴圈的中是j=i。具體的演算法過程如下:

n+(n-1)+(n-2)+(n-3)+……+1
=(n+1)+[(n-1)+2]+[(n-2)+3]+[(n-3)+4]+……
=(n+1)+(n+1)+(n+1)+(n+1)+……
=(n+1)n/2
=n(n+1)/2
=n²/2+n/2
複製程式碼

根據上面說的推導大O階的規則,得到上面這段程式碼的時間複雜度是O(n²)

其他常見覆雜度

f(n)=nlogn時,時間複雜度為O(nlogn),可以稱為nlogn階。

f(n)=n³時,時間複雜度為O(n³),可以稱為立方階。

f(n)=2ⁿ時,時間複雜度為O(2ⁿ),可以稱為指數階。

f(n)=n!時,時間複雜度為O(n!),可以稱為階乘階。

f(n)=(√n時,時間複雜度為O(√n),可以稱為平方根階。

時間複雜度比較

嗯,我們再回頭看下下面的圖片:

comparision_computational_cmplexity

通過圖片直觀的體現,能夠得到常用的時間複雜度按照消耗時間的大小從小到大排序依次是:

O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n³)<O(2ⁿ)<O(n!)

參考

bigocheatsheet.com/

劉望舒 -- juejin.im/post/5bbd79…

李斌 -- zhuanlan.zhihu.com/p/32135157

後話

文章首發 -- github-演算法的時間複雜度

嗯,當年上的演算法課,現在丟掉的總得還,所以自己新建了一個倉庫來管理--github-js_algorithm,有興趣可以喵一下。共勉@~@

相關文章