1. 程式人生 > >用大O記號法測量算法的效率(Algorithm efficiency Asymptotic notation Big O notation)

用大O記號法測量算法的效率(Algorithm efficiency Asymptotic notation Big O notation)

元素 事先 數組 編程導論 一段 運行時間 計算機 初始 算法

為什麽要了解算法的效率?

一般來說,編程就是把各種已知的算法代入到自己的代碼當中,以此來解決問題。因此,了解各種算法的效率對於我們選擇一個合適的算法有很大幫助。

算法的效率由什麽確定?

從算法分析的理論來講,算法的效率通常由它們的復雜度來評估,用漸近記號(asymptotic notation)來表示,通常有 O、 Θ和Ω 記號法。漸進的意思就是當問題的規模變大時,解決這個問題所耗費的時間增加了多少。

(註:當規模較小時,無論是高效的算法還是低效的算法,時間耗費差距不明顯,很可能產生誤導的結果。所以算法分析針對大規模輸入。)

如何測量算法的效率?

一個算法是由控制結構(順序、分支和循環3種)和基本操作(指固有數據類型的操作)構成的,算法的運行時間與算法中語句的執行次數成正比例,某個算法中語句執行次數多,它運行花費的時間就多。比較同一個問題的不同算法的效率,通常的做法是,選取該算法的基本操作,以其基本操作的重復執行次數作為算法的時間量度,記為時間頻度T(n)。n為問題的規模,當n不斷變化時,T(n)也會不斷變化。一般情況下,算法中重復執行基本操作的次數是問題規模n的某個函數,用T(n)表示。若有某個輔助函數f(n),當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函數,記作T(n)=O(f(n))。稱O(f(n)) 為算法的漸進時間復雜度,簡稱時間復雜度。

算法運行的幾種情況(以在一個有n個元素的數列中查找某個元素為例):

  • 最好情況(Best Case):第一次就找到了,表示為O(1)
  • 期望情況(Expected Case):在數列中間的某個位置m找到,表示為O(m)
  • 最壞情況(Worst Case):在數列最後才找到,表示為O(n)

在實際使用中,我們一般僅考量算法在最壞情況下的運行情況,也就是對於規模為 n 的輸入,算法的最長運行時間。這樣做的理由是:

  1. 一個算法在最壞情況下的運行時間是在任何輸入下運行時間的一個上界(Upper Bound),不管其他情況如何,運行時間不會更長
  2. 對於某些算法來說,最壞情況出現的次數還是比較頻繁的,比如在數據庫中檢索一條實際並不存在的記錄
  3. 很多時候,算法的期望情況和最壞情況一樣差。例如插入排序在最壞情況下(數組事先逆序)和平均情況下(假設有一半逆序),復雜度均為O(n2)

大O記號法(big O notation,O代表omicron,為希臘字母第15個字)的定義:

對於規模為 n的輸入,當n增大時,運行這個函數所增加的耗費時間的上界。

算法復雜度的類型:

  • O(1) --- 常量復雜度(constant complexity)
  • O(n) --- 線性復雜度(linear complexity)
  • O(n2) --- 二次方復雜度(quadratic complexity)
  • O(log n) --- 對數復雜度(logarithmic complexity)
  • O(cn) --- 指數復雜度((exponential complexity)

其他還有O(n3)三次方復雜度,O(n!)階乘復雜度等等。

用例子來說明不同類型的算法復雜度:

假設我們現在要自己寫一段代碼來計算ab的結果(b為正整數)。

方法一:

def exp1(a,b):
    ans=1
    while b>0:
        ans*=a
        b-=1
    return ans

此算法基本步驟為:3b+2步(每個循環裏面有3步,一共循環b次,再加上初始ans賦值和返回ans值這兩步)。當b足夠大時,其他數字都不重要了,因此這個算法是一個線性復雜度的算法。

方法二:

def exp2(a,b):
    if b==1:
        return a
    return a*exp2(a,b-1)

此算法基本步驟為:3b-1步(此處省略推算過程,具體可見參考視頻第12分鐘處)。因此這個算法也是一個線性復雜度的算法。

方法三:

def exp3(a,b):
    if b==1:
        return a
    if b%2==0:  # 如果b是偶數
        return (a*a)**(b/2)
    else:   # 如果b是奇數,a的b次方等於a*a**(b-1)
        return a*exp3(a,b-1)

此算法基本步驟為:log b步(此處省略推算過程,具體可見參考視頻第17分鐘處)。因此這個算法是一個對數復雜度的算法。

方法四:

def exp4(a,b):
    ans=0
    for i in range(a):
        for j in range (b):
            ans+=1
    return ans

此算法基本步驟為:b2步(此處省略推算過程,具體可見參考視頻第20分鐘處)。因此這個算法是一個二次方復雜度的算法。

不同類型算法復雜度的時間增長對比:

O(1) --- 輸入增大10倍,解決問題的時間不變

O(n) --- 輸入增大10倍,解決問題的時間相應增大10倍

O(log n) --- 輸入增大10倍,解決問題的時間相應增大1倍

O(n2) --- 輸入增大10倍,解決問題的時間相應增大100倍

參考:麻省理工學院公開課:計算機科學及編程導論 (第8課)

用大O記號法測量算法的效率(Algorithm efficiency Asymptotic notation Big O notation)