算法之時間複雜度

在瞭解時間複雜度以前,讓咱們來了解一下什麼是算法?

算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令(個人理解是一系列解決問題的步驟),算法表明着用系統的方法描述解決問題的策略機制。也就是說,可以對必定規範的輸入,在有限時間內得到所要求的輸出。不一樣的算法可能用不一樣的時間、空間或效率來完成一樣的任務。算法

一個算法的優劣能夠用空間複雜度與時間複雜度來衡量。函數

 

算法的五大特性:

  • 輸入:算法具備0個或多個輸入
  • 輸出: 算法至少有1個或多個輸出
  • 有窮性: 算法在有限的步驟以後會自動結束而不會無限循環,而且每個步驟能夠在可接受的時間內完成
  • 肯定性:算法中的每一步都有肯定的含義,不會出現二義性
  • 可行性:算法的每一步都是可行的,也就是說每一步都可以執行有限的次數完成

 

不一樣的算法對執行程序的結果也許是同樣的,可是執行時間和效率卻有着很大的區別,下面讓咱們來看個例子:性能

問題:a + b + c = 100, a^2  + b^2 = c^2,請計算出全部符合的a, b, c結果。操作系統

import time

start_time = time.time()

# 注意是三重循環
for a in range(0, 1001):
    for b in range(0, 1001):
        for c in range(0, 1001):
            if a**2 + b**2 == c**2 and a+b+c == 1000:
                print("a, b, c: %d, %d, %d" % (a, b, c))

end_time = time.time()
print("time: %f" % (end_time - start_time))
print("finished!")

 結果:blog

a, b, c: 0, 500, 500
a, b, c: 200, 375, 425
a, b, c: 375, 200, 425
a, b, c: 500, 0, 500
time: 214.583347
finished!

程序執行完總共花費了214秒。咱們再來看一個例子:it

import time

start_time = time.time()

# 注意是兩重循環
for a in range(0, 1001):
    for b in range(0, 1001-a):
        c = 1000 - a - b
        if a**2 + b**2 == c**2:
            print("a, b, c: %d, %d, %d" % (a, b, c))

end_time = time.time()
print("time: %f" % (end_time - start_time))
print("finished!")

結果:table

a, b, c: 0, 500, 500
a, b, c: 200, 375, 425
a, b, c: 375, 200, 425
a, b, c: 500, 0, 500
time: 0.182897
finished!

程序執行完總共花費了0.18秒,這對於第一種方法來講,無疑是一種很大的提高。因此說,算法的好壞對於程序來講是很是重要的。class

所以,咱們能夠得出一個結論:實現算法程序的執行時間能夠反應出算法的效率,即算法的優劣。效率

 

單靠時間值絕對可信嗎?

假設咱們將第二次嘗試的算法程序運行在一臺配置古老性能低下的計算機中,狀況會如何?極可能運行的時間並不會比在咱們的電腦中運行算法一的214.583347秒快多少。import

單純依靠運行的時間來比較算法的優劣並不必定是客觀準確的!

程序的運行離不開計算機環境(包括硬件和操做系統),這些客觀緣由會影響程序運行的速度並反應在程序的執行時間上。那麼如何才能客觀的評判一個算法的優劣呢?

 

時間複雜度與「大O記法」

咱們假定計算機執行算法每個基本操做的時間是固定的一個時間單位,那麼有多少個基本操做就表明會花費多少時間單位。算然對於不一樣的機器環境而言,確切的單位時間是不一樣的,可是對於算法進行多少個基本操做(即花費多少時間單位)在規模數量級上倒是相同的,由此能夠忽略機器環境的影響而客觀的反應算法的時間效率。

對於算法的時間效率,咱們能夠用「大O記法」來表示。

「大O記法」:對於單調的整數函數f,若是存在一個整數函數g和實常數c>0,使得對於充分大的n總有f(n)<=c*g(n),就說函數g是f的一個漸近函數(忽略常數),記爲f(n)=O(g(n))。也就是說,在趨向無窮的極限意義下,函數f的增加速度受到函數g的約束,亦即函數f與函數g的特徵類似。

時間複雜度:假設存在函數g,使得算法A處理規模爲n的問題示例所用時間爲T(n)=O(g(n)),則稱O(g(n))爲算法A的漸近時間複雜度,簡稱時間複雜度,記爲T(n)

 

最壞時間複雜度

分析算法時,存在幾種可能的考慮:

  • 算法完成工做最少須要多少基本操做,即最優時間複雜度
  • 算法完成工做最多須要多少基本操做,即最壞時間複雜度
  • 算法完成工做平均須要多少基本操做,即平均時間複雜度

對於最優時間複雜度,其價值不大,由於它沒有提供什麼有用信息,其反映的只是最樂觀最理想的狀況,沒有參考價值。

對於最壞時間複雜度,提供了一種保證,代表算法在此種程度的基本操做中必定能完成工做。

對於平均時間複雜度,是對算法的一個全面評價,所以它完整全面的反映了這個算法的性質。但另外一方面,這種衡量並無保證,不是每一個計算都能在這個基本操做內完成。並且,對於平均狀況的計算,也會由於應用算法的實例分佈可能並不均勻而難以計算。

所以,咱們主要關注算法的最壞狀況,亦即最壞時間複雜度。

 

算法分析

1)上面第一個列子核心部分:

for a in range(0, 1001):
    for b in range(0, 1001):
        for c in range(0, 1001):
            if a**2 + b**2 == c**2 and a+b+c == 1000:
                print("a, b, c: %d, %d, %d" % (a, b, c))

時間複雜度:

T(n) = O(n*n*n) = O(n3)

 

2)第二個例子核心部分:

for a in range(0, 1001):
    for b in range(0, 1001-a):
        c = 1000 - a - b
        if a**2 + b**2 == c**2:
            print("a, b, c: %d, %d, %d" % (a, b, c))

時間複雜度:

T(n) = O(n*n*(1+1)) = O(n*n) = O(n2)

因而可知,咱們嘗試的第二種算法要比第一種算法的時間複雜度好多的。

 

常見時間複雜度

執行次數函數舉例 非正式術語
12 O(1) 常數階
2n+3 O(n) 線性階
3n^2+2n+1 O(n^2) 平方階
5log2n+20 O(logn) 對數階
2n+3nlog2n+19 O(nlogn) nlogn階
6n^3+2n^2+3n+4 O(n3) 立方階
2^n O(2n) 指數階

常見時間複雜度的關係

 

 

所消耗的時間從小到大

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

相關文章
相關標籤/搜索