1. 算法的五大特性
① 輸入: 算法具有0個或多個輸入
② 輸出: 算法至少有1個或多個輸出
③ 有窮性: 算法在有限的步驟之後會自動結束而不會無限循環,並且每一個步驟可以在可接受的時間內完成
④ 確定性:算法中的每一步都有確定的含義,不會出現二義性
⑤ 可行性:算法的每一步都是可行的,也就是說每一步都能夠執行有限的次數完成
算法是獨立存在的一種解決問題的方法和思想。
2. 最壞時間複雜度
① 算法完成工作最少需要多少基本操作,即最優時間複雜度
② 算法完成工作最多需要多少基本操作,即最壞時間複雜度
③ 算法完成工作平均需要多少基本操作,即平均時間複雜度
最優時間複雜度
,其價值不大,因爲它沒有提供什麼有用信息,其反映的只是最樂觀最理想的情況,沒有參考價值;最壞時間複雜度
,提供了一種保證,表明算法在此種程度的基本操作中一定能完成工作。平均時間複雜度
,是對算法的一個全面評價,因此它完整全面的反映了這個算法的性質。但另一方面,這種衡量並沒有保證,不是每個計算都能在這個基本操作內完成。而且,對於平均情況的計算,也會因爲應用算法的實例分佈可能並不均勻而難以計算。
我們主要關注算法的最壞情況,亦即最壞時間複雜度。
3. 時間複雜度的基本計算規則
基本操作,即只有常數項,認爲其時間複雜度爲O(1)
順序結構,時間複雜度按加法進行計算
循環結構,時間複雜度按乘法進行計算
分支結構,時間複雜度取最大值
判斷一個算法的效率時,往往只需要關注操作數量的最高次項,其它次要項和常數項可以忽略。在沒有特殊說明時,我們所分析的算法的時間複雜度都是指最壞時間複雜度。
4. 常見時間複雜度
執行次數函數舉例 | 階 | 非正式術語 |
---|---|---|
12 | O(1) | 常數階 |
2n+3 | O(n) | 線性階 |
3n2+2n+1 | O(n2) | 平方階 |
5log2n+20 | O(logn) | 對數階 |
2n+3nlog2n+19 | O(nlogn) | nlogn階 |
6n3+2n2+3n+4 | O(n3) | 立方階 |
2n | O(2n) | 指數階 |
經常將log2n(以2爲底的對數)簡寫成logn。
5. 常見時間複雜度之間的關係
所消耗的時間從小到大:O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n2log(n)) < O(n3) < O(2n) < O(n!) < O(nn)
6. 算法分析
如果 a+b+c=1000,且 a2+b2=c^2(a,b,c 爲自然數),如何求出所有a、b、c可能的組合?
① 第一種算法:
import time
start_time = time.time()
for a in range(1001):
for b in range(1001):
for c in range(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('%d' % (end_time - start_time))
"""
a, b, c: 0, 500, 500
a, b, c: 200, 375, 425
a, b, c: 375, 200, 425
a, b, c: 500, 0, 500
576
"""
時間複雜度:T(n) = O(nnn) = O(n3)
② 第二種算法:
import time
start_time = time.time()
for a in range(1001):
for b in range(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(end_time - start_time)
"""
a, b, c: 0, 500, 500
a, b, c: 200, 375, 425
a, b, c: 375, 200, 425
a, b, c: 500, 0, 500
0.4090738296508789
"""
時間複雜度:T(n) = O(nn(1+1)) = O(n*n) = O(n2)