如何進行算法分析呢?
最簡單方法是分别運行解決同一個問題的兩個算法進行比較,但這樣做法在很多時候并不理想。面對這樣的困難迫使我們求助于數學工具,雖然我們不能對一個還沒有完整實現的程序使用運行比較法,但卻能通過數學分析了解程序性能的大緻輪廓并預估改進版本的有效性。
大多數算法都有影響運行時間的主要參數 N,這裡的 N 是所解決問題的大小的抽象度量,比如對于一個排序算法來說,N 是待排序元素的個數。我們的目标是盡可能地使用簡單的數學公式,用 N 表達出程序的運行效率。
函數的增長對于将要比較的兩個算法,我們并不滿足于簡單地描述為“一個算法比另一個算法快”,而是希望能夠通過數學函數直觀地感受到二者的差異,具體來說,是希望知道“一個算法比另一個算法快多少”。
一些函數在算法分析中極為常見:
來看一下這些函數的增長曲線,如圖 1 所示。
▲ 圖 1 函數增長的曲線
以上函數能夠幫助我們直觀地理解算法的運行效率,讓我們很容易區分出快速算法和慢速算法。大多數時候,我們都簡單地把程序運行的時間稱為“常數”、“線性”、“平方次”等。對于小規模的問題,算法的選擇不那麼重要,一旦問題達到一定規模,算法的優劣就會立馬體現出來。代碼 4-2 展示了當問題規模是 10、100、1000、10000、100000、1000000 時 lgN 、√N 、N、N lgN 、N² 、N³ 的增長規模。
▼代碼 2 函數的增長規模 C4_2.py
01importmath
02
03fun_list=['lgN','sqrt(N)','N','NlgN','N^2','N^3']#函數列表
04print(''*10,end='')
05forfinfun_list:
06print('%-15s'%f,end='')
07print('\n','-'*100)
08
09N_list=[10**nforninrange(7)]#問題規模
10forNinN_list:#函數在不同問題規模下的增長
11print('%-8s%-2s'%(N,'|'),end='')
12print('%-15d'%round(math.log2(N)),end='')
13print('%-15d'%round(math.sqrt(N)),end='')
14print('%-15d'%N,end='')
15print('%-15d'%round(N*math.log2(N)),end='')
16print('%-15d'%N**2,end='')
17print(N**3)
運行結果如圖 4.2 所示。
圖 2 告訴我們,該問題規模是 1 的時候,所有算法都同樣有效,問題規模越大,不同複雜度的算法運行效率相差得越大。
2^N 增長得太過迅猛,作為一個另類單獨列出。
▼ 代碼 3 2^N 的增長
01forNinrange(10,110,10):
02print('2**{0}={1}'.format(N,2**N))
運行結果如圖 3 所示。
▲ 圖 4.3 2^N 的增長
這些運行結果告訴我們,有些時候,選擇正确的算法是解決問題的唯一途徑。對于函數的輸出結果來說,如果把 100 看作 1 秒,那麼 10000 就是 100 秒,超過 1 分半。這意味着對于一個規模是 1000000 的問題來說,一個是 lgN 複雜度的算法可以立刻得出結果,√N 複雜度的算法耗時約 10 秒,N 複雜度的算法耗時将超過 2.7 小時,N^3 複雜度則需要 3 萬多年!也許我們可以忍受一運行 10 秒或 2.7 小時的程序,但一定沒法容忍有生之年看不到結果的程序。
上文 [遇見] 授權節選自北大出版社《程序員數學從零開始》
,270餘幅插圖 90餘段Python代碼 20餘個原理剖析,教你學會程序員必須掌握的數學及算法背後的數學原理。
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!