前言
在算法的學習中,最開始便是要學習算法的分析。學習算法分析時,我們便會接觸到這麼幾個符號:大O、大Ω、大θ,常常讓人難以理解。
在通常的算法分析時,我們可以明白,在輸入規模較小,各種算法之間的時間消耗並無明顯差別。只有當輸入規模較大時,對各個算法之間消耗差別的對比與分析纔有意義。所以上面幾個符號便常用於表達當規模逐漸趨向於一個極大數時的算法複雜度。
在表示一個算法時間複雜度時,我們常用如 T(n)=O(n^2) 的形式表示,而在漸進分析中的 “=” 更傾向於 “∈” 的意思。打個比方:漸進表達式 f(n) = O(g(n)) 所表達的意思是 O(g(n)) = [ f(n),h(n),…,g(n) ], f(n) ∈ O(g(n)) 。
一、大O表示法
f(x) = O(g(x)) 表示的含義是f(x)以g(x)爲上界
大O是我們在分析算法複雜度時最常用的一種表示法。當函數的大小隻有上界,沒有明確下界的時候,則可以使用大O表示法,該漸進描述符一般用與描述算法的 最壞複雜度。f(x) = O(g(x))正式的數學定義:存在正常數c、n、n0,當 n>n0 的時,任意的 f(n) 符合 0 <= f(n) <= c.g(n)。
座標圖如下:
示例:
下面是一個很簡單的嵌套循環,在分析這種簡單算法的複雜度時,我們通常計算其中 關鍵步驟的執行次數 作爲此算法的時間複雜度。
分析一: 該算法外層執行了 n 次循環,如果內層也是 n 次循環,我們便可知道該算法時間複雜度爲 n^2,但是該算法內層執行的循環次數會隨着外層循環的進行依次減少,最大爲n。所以,我們便可以確定該算法的時間複雜度有一個上界 n^2,即T(n) = O(n^2)。
分析二: 另外我們可以採用最簡單的加法來判定該算法複雜度,隨着外層的循環,內層執行的次數依次爲 n、n-1、…、2、1,那麼關鍵步驟總的執行次數爲等差數列的和 (n+1)*n/2, 展開即 n^2/2 + n/2,以此也可確定一個上界n^2,在我們使用O時,常有的一個說法便是“ O爲取最高次數項去掉係數 ”。
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
... // 關鍵步驟
}
}
二、大Ω表示法
f(x) = Ω(g(x)) 表示的含義是f(x)以g(x)爲下界
當函數的大小隻有下界,沒有明確的上界的時候,可以使用大Ω表示法,該漸進描述符一般用與描述算法的 最優複雜度 。 f(n)= Ω(g(n)) 正式的數學定義:存在正常數c、n、n0,當 n > n0 的時,任意的 f(n) 符合 0 <= c.g(n) <= f(n)。
座標圖如下:
簡析:
爲什麼我們通常把O用於分析最壞複雜度,把Ω用於分析最優複雜度?
大Ω描述的界是漸進最小,加入用大Ω來描述最壞複雜度,因爲這只是一個下界,並不能說明算法的最壞複雜度,所以沒意義。大O同理。
三、大θ表示法
f(x) = Θ(g(x)) 表示的含義是g(x)是f(x)的確界
用於界定函數的漸進上界和漸進下界。當 f(n)= θ(g(n)) 的時候,代表着g(n)爲f(n)的漸進緊確界。而θ漸進描述符在所有的漸進描述符中是最嚴格的一個,因爲它既描述了函數的上界,有描述了函數的下界。
f(n)= θ(c.g(n)) 正式的數學定義:存在正常數c1、c2、n、n0,當 n > n0 的時,對於任意的f(n)對符合 c1.g(n) <= f(n) <= c2.g(n),c1.g(n)、c2.g(n)都是漸進正函數(當n趨於無窮大的時候,f(n)爲正)。
座標圖如下:
算法導論中還根據大O,大Ω,大θ的定義得到以下定理:
當且僅當函數 f(n) = O(g(n)) 並且 f(n)= Ω(g(n)) 時,纔有 f(n) = θ(g(n))。
常見的幾個算法複雜度
上圖橫軸代表輸入規模,數軸代表複雜度,由圖中的時間複雜度增長趨勢可以看出:
- 常見的最優的算法複雜度爲logN;
- 當 N 的值較小時,NlogN < N, 當 N 的值較大時,NlogN > N;
- 指數次方與階乘的複雜度非常高,應當儘量避免使用這些算法,二次方或三次方的時間複雜度在N值較大是也是相當高的,在設計算法的時候必須要注意這一點;