算法及算法的評價
算法
算法是對特定問題求解步驟的一種描述,它是指令的有點序列,其中每一條指令表示一個或多個操作。
算法的五個重要特性
有窮性、確定性、可行性、具有輸入、具有輸出。
好算法的特征
通常一個好的算法應該達到以下目標:
1.正確性:算法應當能正確地解決求解問題。
2.可讀性:算法應當具有良好的可讀性,以助人們理解。
3.健壯性:當輸入非法的數據時,算法也能適當的做出反應或進行處理,而不會產生莫名奇妙的輸出結果。
4.效率與低存儲量需求:效率是指算法的執行時間、存儲量需求是指算法執行過程中所需要的最大存儲空間,這兩者都與問題規模有關。
漸進分析:復雜度和大O記號的引入
回歸最初的問題,隨着問題的增長,計算成本應該如何增長?這里的我們只關心足夠大的問題規模,注重考察成本的增長趨勢。
漸進分析:在問題規模足夠大后,計算成本增長的分析。
時間復雜度和空間復雜度
是算法的效率的度量。
時間復雜度:一個語句的頻度是指該語句在算法中被重復執行的次數。算法中的所有語句的頻度之和記作\(T(n)\),它是該算法問題規模n的函數,時間復雜度主要分析\(T(n)\)的數量級,又因為算法中基本運算(最深層循環內的語句)的頻度與\(T(n)\)同數量級,所以通常采用算法中基本運算的頻度\(f(n)\)來分析算法的時間復雜度。因此,算法的時間復雜度記為:\(T(n) = O(f(n))\)
大O記號(big-O notation)
\(T(n) = O(f(n)) \quad\) \(iff \quad\) \(∃ \ c > 0\),當 \(n >> 2\)后,有\(T(n) < c*f(n)\)
\(\sqrt{5n*[3n*(n+2) + 4] + 6} < \sqrt{5n * [6n^2 + 4] + 6} < \sqrt{35n^3 + 6} < 6 * n^{1.5} = O(n^{1.5})\)
與\(T(n)\)相比,\(f(n)\)更為簡潔,但依然反映着前者的增長趨勢。
常系數可忽略:\(O(f(n)) \ = \ O(c*f(n))\)
低次項可忽略:$O(n^a + n^b) = O(n^a) \(, 當\)a > b > 0$
時間復雜度:\(O(1)\)
常數(constant function)
\(2 = 2013 = 2013 * 2013 = O(1)\),甚至\(2013^{2013} = O(1)\) //含RAM各基本操作
這類算法效率最高(總不能奢望不勞而獲吧)
什么樣的代碼段對應於常數執行時間? (應具體分析)
一定不含循環?
for(i = 0; i < n; i += n / 2013 + 1);
for(i = 1; i < n; i = 1 << i) // log*n,幾乎為常數
一定不含分支轉向?
if((n + m) * (n + m) < 4 * n * m) goto UNREACHABLE; //不考慮溢出
一定不能有(遞歸調用)?
if(2 == (n * n) % 5) O1(n);
時間復雜度:\(O(log^cn)\)
**對數\(log(n)\) **
\(lnn\)| $ lgn $| $log_{100}n $| \(log_{2013} n\)
**常底數無所謂 **
\({\forall}\ a,\ b > 0, \ log_an = \ log_ab * \ log_bn = \ Θ(log_bn)\)
**常數次冪無所謂 **
\({\forall}\ c \ > \ 0 ,\ logn^c \ = \ c * logn = Θ(logn)\)
**對數多項式(ploy-log function) **
\(123*log^{321}n \ + \ log^105(n^2 - n + 1) \ =\ Θ(log^{321}n)\)
這類算法非常有效,復雜度無限接近於常數:\({\forall} \ c \ > \ 0, \ logn \ = \ O(n^c)\)
時間復雜度:\(O(n^c)\)
**多項式(ploynomial function) : **
\(100n + 200 = O(n)\)
\((100n - 500)(20n^2 - 300n = 2013) \ = \ O(n * n^2) \ = \ O(n^3)\)
\((2013n^2 -20) / \ (1999n - 1) \ = \ O(n^2/n) \ = O(n)\)
一般地:\(a_kn^k + a_{k-1}n^{k-1} + ... + a_1n + a_0 \ = \ O(n^k), \ a_k > 0\)
線性(linear function):所有\(\ O(n)\)類函數
從\(O(n)\)到\(O(n^2)\):編程算法題的主要覆蓋范圍
冪:\([(n^{2013} -24n^{2009})^{1/3} + 512n^{567} - 1978n^{123}]^{1/11} \ = \ O(n^{61})\)
時間復雜度:\(O(2^n)\)
**指數(exponential function): **\(T(n) \ = \ a^n\)
\({\forall} \ c \ > \ 1,\ n^c \ = \ O(2^n)\)
\(n^{1000} \ = \ O(1.0000001^n) \ = \ O(2^n)\)
\(1.0000001^n = Ω(n^{1000})\)
這類算法的計算成本增長極快,通常被認為不可忍受。
從\(O(n^c)\)到\(O(2^n)\)是從有效算法到無效算法的分水嶺,很多問題\(O(2^n)\)的算法往往顯而易見,然后設計出\(O(n^c)\)的算法卻極其的不易,甚至,有時候注定是徒勞無功的,我們把這些問題分為NP和非NP問題。
復雜度層次
最壞時間復雜度:指在最壞的情況下,算法的時間復雜度。
平均時間復雜度:指所有輸入等可能的情況下,算法的期望運行時間。
最好時間復雜度:值在最好的情況下,算法的時間復雜度。
均攤時間復雜度:在代碼執行的所有復雜度情況中絕大部分是低級別的復雜度,個別情況是高級別復雜度且發生具有時序關系時,可以將個別高級別復雜度均攤到低級別復雜度上。基本上均攤結果就等於低級別復雜度。
舉例:有一個長度為n的數組,如果數組沒滿,就往里插入一個數,如果數組滿了,就遍歷求和.那么絕大多數情況下都是\(O(1)\),只有最后一次是\(O(n)\),均攤以后就是\(O(1)\)
復雜度振盪:數組等數據結構的擴容和縮容之間出現,原因是沒有處理好擴容和縮容因子之間的關系。
平均分析 VS 分攤分析
平均復雜度或期望復雜度(average/expected complexity)
根據數據結構各種操作出現概率的分布,將對應的成本加權平均。
各種可能的操作,作為獨立事件分別考察,割裂了操作之間的相關性 和連貫性,往往不能准確地評判數據結構和算法的真實性能。
分攤復雜度(amortized complexity)
對數據結構連續地實施足夠多次的操作,所需總體成本分攤至單次操作。
從實際可行的角度,對一系列操作做整體的考量,更加忠實地刻畫了可能出現的操作序列,可以更為精准地評判數據結構和算法的真實性能。
空間復雜度
算法的空間復雜度\(S(n)\),定義為該算法所耗費的輔助存儲空間,它是問題規模n的函數。漸進空間復雜度通常簡稱為空間復雜度,記作\(S(n) = O(g(n))\)。
算法的 原地工作是指算法所需的輔助存儲空間是常量,即O(1)。