0. 時間復雜度
接觸到算法的小伙伴們都會知道時間復雜度(Time Complexity)的概念,這里先放出(漸進)時間復雜度的定義:
假設問題規模是\(n\),算法中基本操作重復執行的次數是\(n\)的某個函數,用\(T(n)\)表示,若有某個輔助函數\(f(n)\),使得
其中\(c\)為不等於零的常數,則稱\(f(n)\)是\(T(n)\)的同數量級函數。記作\(T(n)=O(f(n))\),稱\(O(f(n))\) 為算法的漸進時間復雜度,簡稱時間復雜度。
常見的時間復雜度有(表格越靠后表示越不理想):
復雜度 | 名稱 |
---|---|
\(O(1)\) | 常數階 |
\(O(\log n)\) | 對數階 |
\(O(n)\) | 線性階 |
\(O(n\log n)\) | 線性對數階 |
\(O(n^2)\) | 平方階 |
\(O(n^3)\) | 立方階 |
\(O(n^k)\) | \(k\)次方階(\(k>3\)且\(k\in Z\)) |
\(O(2^n)\) | 指數階 |
例如,我們熟悉的插入排序(Insertion Sort)算法的時間復雜度是\(O(n^2)\),而合並排序(Merge Sort)算法的時間復雜度是\(O(n\log n)\)
那么這些復雜度之間的差距是怎么樣的呢?有些小伙伴會疑問,自己寫的算法雖然是高復雜度但是也用的好好的,為什么要糾結於這個概念呢?
我們不妨來探索一下今天的問題:\(O(n^2)\)和\(O(n\log n)\)差距有多大?
1. \(O(n^2)\)和\(O(n\log n)\)差距有多大?
我們知道,插入排序(Insertion Sort)算法的時間復雜度是\(O(n^2)\),而合並排序(Merge Sort)算法的時間復雜度是\(O(n\log n)\),即當排序\(n\)個對象時,插入排序算法需要用時大約\(c_1n^2\),而合並排序算法需要用時大約\(c_2n\log{n}\),其中\(c_1\)和\(c_2\)都是正常數且與\(n\)無關,且往往\(c_1<c_2\)。
稍微利用初等數學的知識,可以知道,對於任何\(n>=2\),比較約\(c_1n^2\)和\(c_2n\log{n}\)即比較\(c_1n\)和\(c_2\log{n}\)。由於我們已知
以及
想要比較這兩個值的大小,直觀的看法就是比較兩個不等式誰的差別“更多”。可以證明,當無論\(c_1\)和\(c_2\)差別多么顯著,總存在充分大的\(N\)使得當\(n>N\)時,\(c_1n>c_2\log{n}\)。
在Introduction to Algorithms中,作者舉了一個很有趣的例子:
假設針對同一排序問題,用一台很快的電腦A運行插入排序,用一台很慢的電腦B運行合並排序,問題規模\(n=10^7\):
兩台電腦的差別如下,為了使A比B優勢顯著,作者假設電腦A性能比B強1000倍,並且B運行的代碼更低效、且編譯器更差(導致需要運行更多的指令):
電腦A | 電腦B | |
---|---|---|
每秒運行指令數 | \(10^{10}\) | \(10^7\) |
需要運行的指令總數 | \(2n^2\) | \(50n\log n\) |
這樣,A完成任務需要:
而B完成任務需要:
可以看到,在這樣的大規模的問題下,即便B計算機與A差距巨大,最終也只用了20分鍾左右就完成排序,而A卻需要5.5小時來完成。時間復雜度的差距可見一斑。
3. 總結
算法時間復雜度的量級差異,也許在小規模的問題下,表現差別不大。但是時間復雜度高的算法,對問題規模的變化更加敏感,因而當問題的規模變得很大的時候,靠擁有高階時間復雜度的算法來求解並不可靠!
(更新)我從網絡上找到了一個直觀的各個階的復雜度的對比,大家不妨參考一下:
# 喜歡就點個贊、關注支持一下吧!
參考:
Thomas H. Cormen, et al., Introduction to Algorithms Part I 1.2
http://www.bigocheatsheet.com