乙個演算法的複雜度可以說也就是乙個演算法的效率,一般來說分為時間複雜度和空間複雜度。。。
注意接下來說的均是比較yy的,適用與acm等不需嚴格分析只需要大致範圍的地方,至於嚴格的演算法複雜度分析的那些數學證明,主定理什麼的在《演算法導論》這本書上有十分詳細的講解,網上應該也會有人寫過,這裡就不多說了(其實,是我不會而已o(╯□╰)o。。。)。
— 到底啥是複雜度呢?先來個栗子。
小明有10個蘋果,有一天他餓了,然後準備吃掉乙個蘋果,但是小明有中二病,他要吃裡面重量最大的那個,於是。。。他需要乙個找到那個最大的,可是這應該怎麼找呢?
小明要先量一下第乙個蘋果多重,然後第二個多重,然後量第三個。。。一直到第十個,量的時候記錄當前最重的那個,然後當找完了這十個就好了。。。
下面來看看這個神奇的找蘋果演算法的複雜度,有10個蘋果,所以需要測量10次才能找到,如果有100個蘋果,顯然需要測量100次,1000個呢,1000次,n個也就需要n次。
下面設n為問題的規模,f(n)為運算次數,那麼對於小明這個問題來說 f(n)=kn,k是乙個常數,這個例子 k=1。(看到如此眼熟的高中數學氣息有沒有啥感覺。。。)
— 不用多說就知道如果需要的運算次數多的話,需要花費的時間也就多,所以這就是時間複雜度了,對於上面那個問題的時間複雜度就是 f(n) 了。
— 但是。。。對於乙個問題的常數 k 是比較難搞定的,可能稱一下蘋果的重量需要一步操作,也可能兩步,也可能好幾步。所以對於時間複雜度的分析一般是去找漸進複雜度。簡單說就是函式 f(n) 省略了常數和低次項,只留下最高次項。比如函式 6n^3+3n^2+2n+10 變成了 n^3 ,因為當n很大很大的時候,變化趨勢就是 n^3 型的,再比如 2^n+n^5 就變成了 2^n,因為指數**嘛。。。
— 這裡還要說說符號 o,o 和 θ,這三個應該高數課會講。。。o( f(n) ) 表示函式的上界,也就是乙個一直比 f(n) 大的函式,然後 o 就是下界,至於 θ 的話,叫做中界(我yy的乙個名字)?也就是和 f(n) 的增長速度一樣。。。不過一般來說之後的分析都是用 o 的,因為這個字母好寫。。。餓,其實可以理解為因為o是上界,所以演算法遇到再壞的情況也不會超過這個函式。
— 對於乙個演算法來說一般常用的漸進複雜度函式有 o( n ) o( n^2 ) o( n^3 ) o(1) o( 2^n ) o( n!) o( log n) o( n*logn ) o( n*2^n ) 差不多這些,注意 log 指以2為底的對數。。。函式圖就像下面這樣:
— 然後分別比較看看哪種複雜度更好,顯然 o(1)是最好的,因為不管問題的規模有多大,都能一下子得到答案。。。然後看看 o(n),小明的問題就是這個複雜度的,如果問題規模是n,需要執行n次,顯然已經不錯了,挺快的了。。。但是還有乙個更快的,o(log n)的,如果n=1000000 那麼才只需要執行 20次不到就能得到答案,但是 o(n)的卻需要執行1000000次,你說哪個快。
舉個栗子:
要求輸入乙個n,然後算 1^2+2^2+3^2+4^2+...+n^2的值。
那麼先說一種做法,直接for迴圈,**如下:
#include using顯然這種演算法的複雜度是 o (n) 的,因為執行了 n 次嘛。(注意 n 比較大的時候結果會超過 int 的表示範圍,具體看 acm錄 常識與錯誤那篇文章有說。)namespace
std;
intmain()
然後看看第二種做法:推出公式來。1^2+2^2+。。。+n^2 = n*(n+1)*(2n+1)/6
所以第二種做法就是
#include using這就是 o ( 1 ) 的複雜度了,那麼哪個快就不說了。。。namespace
std;
intmain()
— 至於後面的 o( n^2 ) o( n^3 ) o( 2^n ) 啊啥的,也是這樣算,上面那些裡面複雜度最高,效率最差的應該是 o(n!),如果 n 是10,就需要執行 3628800 次,這效率,不多說了。。。
— 上面說的是執行的次數,然後說說時間,對於現在的個人計算機來說,速度大約是一秒能執行10000000(7個0)次多,這個可以自己寫個程式感受一下,for迴圈10000000次或者更多看看。。。一般來說如果只有加減法 100000000(8個0)次也很快,但是如果有了除法或者其他東西會慢一點。。。
#include using— 然後對於乙個題目來說一般限制了1秒啊2秒啊啥的,那麼如果題目的資料規模是100000,那麼如果採用的演算法是 o(n^2)的,那麼就需要執行 10000000000次,也就需要大約1000秒,顯然超時了。。。如果演算法是 o(n log n)的,那麼大約是 1700000 次還是可以接受的。。。至於 o(n) o(1) o(log n)啥的就更不用說了。。。所以說解乙個題目的話要注意資料範圍和時間限制。。。namespace
std;
intmain()
— 這裡順便說下常數吧,之前都是把常數忽略了然後看的是乙個大致的增長函式。但是如果常數很大,比如演算法只有乙個 for 迴圈,但是裡面有1000次運算,那麼雖然他的複雜度是 o(n)的,但是對於100000的規模需要執行100000000次,也就很可能會超時了。。。所以當常數比較大的時候要注意看看。。。
— 至於空間複雜度的話,也就是用的空間的多少和資料規模n的函式,但是因為這個不是很常用而且和時間複雜度幾乎差不多,就不多說了。。。
— 那麼怎麼算複雜度呢,在一些演算法和數學的書上有十分嚴格的數學方法。。。然而,平時不需要的。。。
— 其實靠肉眼看看就差不多知道了,看看有幾個迴圈,大致估算一下執行多少次,然後就知道了。。。這些當寫**前想演算法的時候其實就已經大致了解了。。。
— 當然這裡對於存在遞迴的演算法,就比較麻煩了,這裡建議大家學了一些基本的演算法之後再來看,因為這裡實在是找不到簡單的例子。這裡有乙個主定理(名字就叫主定理),用來計算遞迴的函式的複雜度計算。比如說乙個演算法是把問題分成兩個小問題,然後在花費 g(n) 的複雜度來合併兩個小問題得到大問題的解。那麼函式差不多是 f(n)=2*f(n/2)+g(n) 至於 f(n) 怎麼推,就可以用上面的那個主定理(這裡可以去網上找找這個定理學習一下),其實。。。也可以先猜一下,然後帶入那個等式看看行不行。。。
— 經典的遞推比如 f(n)=2f(n/2)+n 的複雜度就是 o(n log n)。。。所以如果 g(n) 如果比 o(n) 要好的話,顯然最後的複雜度會比 o(n log n)更好。。。
— 另外還有一些複雜度分析比如均攤分析就更喪心病狂了,這種分析是找平均值,期望值啥的,通過概率或者是其他什麼東西證明執行比如100次的平均複雜度一定不會高於乙個數,所以平均就是多少多少的,這個的話就不詳細多說了,之後學習比較高階的演算法的時候才會接觸到。。。(其實是我不會。。。)
複雜度分析感覺就這些東西,其實大部分演算法的複雜度是一眼就能看出來,當然也有一些需要仔細分析這個演算法的各種情況才行。。。
演算法分析之複雜度
時間複雜度 是度量演算法執行的時間長短或者說是程式執行的次數。詳細說明 乙個演算法,處理n條資料需要的時間可以用表示式 a n b來表示的話,稱它的時間複雜度為o n 也就是說,100條資料需要1秒的話,1000條資料需要10s。如果是用表示式 a n n b n c的話,複雜度為o n的平方 這樣...
資料結構之複雜度分析
目錄 1 為什麼需要複雜度分析?2 大o複雜度表示法 3 時間複雜度 3.1 只關注迴圈執行次數最多的一段 3.2 總的複雜度等於量級最大的那段 的複雜度 加法法則 3.3 巢狀 的複雜度等於巢狀內外 複雜度的乘積 乘法法則 3.4 時間複雜度 4 空間複雜度 5 時間複雜度擴充套件 網上一直有乙個...
演算法複雜度分析
分析非遞迴演算法效率的通用方案 1.決定用哪個 哪些 引數作為輸入規模的度量 2.找出演算法的基本操作 作為一規律,它總是位於演算法的最內層迴圈中 3.檢查基本操作的執行次數是否只依賴輸入規模。如果它還依賴一些其他的特性,則最差效率 平均效率以及最優效率 如果必要 需要分別研究。4.建立乙個演算法基...