3.算法的渐进分析
算法的漸進(jìn)分析(asuymptotic algorithm analysis)
簡(jiǎn)稱算法分析。算法分析直接與它所求解的問題的規(guī)模n有關(guān),因此,通常將問題規(guī)模作為分析的參數(shù),求算法的時(shí)間和空間開銷與問題規(guī)模n的關(guān)系。
漸進(jìn)的時(shí)間復(fù)雜度
計(jì)算程序執(zhí)行頻度的目的是相比較兩個(gè)或多個(gè)完成相同功能的程序的時(shí)間復(fù)雜度,并估計(jì)但問題規(guī)模變化時(shí),程序的運(yùn)行時(shí)間如何隨之變化。
要想確定一個(gè)程序的準(zhǔn)確的執(zhí)行頻度有時(shí)是非常困難的,而且也不是很必要。因?yàn)槌绦驁?zhí)行頻度這個(gè)概念本身就不是一個(gè)精確的概念。如賦值語(yǔ)句x=a和x=a+b*(c-d)-e/f居然有相同的執(zhí)行頻度。
由于執(zhí)行頻度不能確切地反映運(yùn)行時(shí)間,所以用精確的程序執(zhí)行頻度來比較兩個(gè)程序,其結(jié)果不一定有價(jià)值。
如程序1-20所示遞歸求和算法的執(zhí)行頻度為2n+2,如果把它改成非遞歸程序,其語(yǔ)句執(zhí)行頻度反而是2n+3,比遞歸程序的運(yùn)行時(shí)間還要多,這是不合理的。
因此,只要給出算法的執(zhí)行頻度的數(shù)量級(jí),從n增長(zhǎng)過程中分析算法執(zhí)行次數(shù)增大的數(shù)量級(jí),即可達(dá)到分析的目的。
大O表示
在多數(shù)情況下,只要得到一個(gè)估計(jì)值就足夠了。若沒有問題的規(guī)模為n,程序的時(shí)間復(fù)雜度為T(n),當(dāng)n增大時(shí),T(n)也隨之變大。可是T(n)將如何精確地變化,需要分析程序內(nèi)部結(jié)構(gòu),使用大O表示法描述該程序時(shí)間復(fù)雜度的估計(jì)值。
大O表示法的一般提法是:當(dāng)且僅當(dāng)存在正整數(shù)c和n0,使得T(n)≤cf(n)對(duì)所有n≥n0成立,則稱該算法的時(shí)間增長(zhǎng)率在O(f(n))中,記為T(n)=O(f(n))。
就是說,隨著問題規(guī)模n逐步增大,算法的時(shí)間復(fù)雜度也在增加。從數(shù)量級(jí)大小考慮,算法的程序語(yǔ)句的執(zhí)行次數(shù)(是n的函數(shù))是最壞情況下存在一個(gè)增長(zhǎng)的上限,即cf(n),即算法的增長(zhǎng)率的上限為O(f(n)。
[例 1-21] 考察f(n)=3n+2。當(dāng)n≥2時(shí),3n+2≤3n+n=4n,所以f(n)=O(n),f(n)是一個(gè)變化的函數(shù)。
[例 1-22] 考察f(n)=10n2+4n+2.當(dāng)n≥2時(shí),有10n2+4n+2≤10n2+5n,當(dāng)n≥5時(shí),有5n≤n2,因此,對(duì)于n≥5,f(n)≤10n2+n2=112,f(n)=O(n2)。
[例 1-23] 考察f(n)=6×2^n+n2。當(dāng)n≥4,有n2≤2 ^n,所以對(duì)于n≥4,有f(n)≤6×2 ^n+2 ^n=7×2 ^n,因此f(n)=O(2 ^n).
[例 1-24] 考察f(n)h=9。當(dāng)n0-0,c=9,即可得到f(n)=O(1)。
可以這樣理解:假設(shè)f(n)=2n3+2n2+2n+1,當(dāng)n充分大時(shí),T(n)=O(n3),這是因?yàn)楫?dāng)n很大時(shí),與n3相比,n2與n的數(shù)值常常不產(chǎn)生決定影響,可以忽略不計(jì)。因此,使用大O表示法時(shí),對(duì)于多項(xiàng)式只保留最高次冪的項(xiàng),常數(shù)系數(shù)和低階項(xiàng)可以不要。
為使用大O表示法對(duì)一個(gè)較復(fù)雜的算法的漸進(jìn)時(shí)間復(fù)雜度進(jìn)行度量,一個(gè)簡(jiǎn)捷的方法是分析關(guān)鍵操作的語(yǔ)句執(zhí)行次數(shù),找出其與n的函數(shù)關(guān)系f(n),從而得到漸進(jìn)時(shí)間復(fù)雜度。
關(guān)鍵操作大多存在于循環(huán)和遞歸中。關(guān)于遞歸的討論,參照前面例1-20中遞歸求和程序語(yǔ)句頻度進(jìn)行遞推的過程,不再做進(jìn)一步的討論。下面僅針對(duì)循環(huán)進(jìn)行討論。
對(duì)于單個(gè)循環(huán)而言,在循環(huán)內(nèi)的簡(jiǎn)單語(yǔ)句即為關(guān)鍵操作,該程序段的漸進(jìn)時(shí)間復(fù)雜度應(yīng)是此關(guān)鍵操作的執(zhí)行次數(shù)的大O表示;對(duì)于幾個(gè)并列的循環(huán),先分析每個(gè)循環(huán)的漸進(jìn)時(shí)間復(fù)雜度,然后利用大O表示法的加法規(guī)則來計(jì)算其時(shí)間復(fù)雜度。
大O表示法的加法規(guī)則是指當(dāng)兩個(gè)并列的程序段的時(shí)間代價(jià)分別為T1(n)=O(f(n))和T2(m)=O(g(m))時(shí),那么將兩個(gè)程序段連在一起后整個(gè)程序段的時(shí)間代價(jià)為:
T(n,m)=T1(n)+T2(m)=O(max{f(n),g(m)})
對(duì)于嵌套循環(huán),關(guān)鍵操作應(yīng)在最內(nèi)層循環(huán)中。先自外向內(nèi)層層分析每層循環(huán)的漸進(jìn)時(shí)間復(fù)雜度,然后利用大O表示法的乘法規(guī)則來計(jì)算其漸進(jìn)時(shí)間復(fù)雜度。
大o表示法的乘法規(guī)則是指當(dāng)兩個(gè)嵌套的程序段的時(shí)間代價(jià)分別是T1=O(f(n))和T2(m)=O(g(m))時(shí),整個(gè)程勛段的時(shí)間代價(jià)為
T(n,m)=T1(n)×T2(m)=O(f(n)×g(m))
[例 1-25] 有一個(gè)包含n個(gè)整數(shù)的數(shù)組A,設(shè)計(jì)一個(gè)算法,刪除多余的重復(fù)整數(shù)。算法的實(shí)現(xiàn)參看下面的程序1-23。為避免每檢測(cè)到一個(gè)重復(fù)整數(shù),都要整體移動(dòng)其后的所有整數(shù),先對(duì)多余的重復(fù)整數(shù)做刪除標(biāo)記,待所有整數(shù)都加測(cè)完,再一次性地做數(shù)據(jù)移動(dòng),把做過的刪除標(biāo)記的整數(shù)全部移去。算法返回刪除后整數(shù)個(gè)數(shù)n。
[程序 1-23] 在數(shù)組A中刪除重復(fù)的整數(shù)。
#include <limits.h> //有32b最小整數(shù)常數(shù)_I32_MIN #define delTag_I32_MIN //定義刪除標(biāo)記 void remove Redundancy(int A[],int &n){ //刪除數(shù)組A中所有多余的重復(fù)整數(shù)。有重復(fù)事僅保留其最初出現(xiàn)的整數(shù)int i,j,k=0;for(i=0;i<n;i++) //檢測(cè)所有整數(shù)for(j=i+1;j<n;j++)if(A[i]==A[j])A[j]=delTag; //對(duì)重復(fù)數(shù)刪除標(biāo)記for(i=0;i<n;i++)if(A[i]!=delTag){if(i!=k)A[k]=A[i];k++;}n=k; }算法中有兩個(gè)并列的循環(huán)結(jié)構(gòu)。第一個(gè)循環(huán)結(jié)構(gòu)是兩層嵌套循環(huán),其關(guān)鍵操作是最內(nèi)層的if語(yǔ)句(程序第8行)。它的執(zhí)行頻度是
它服從函數(shù)f(n)=n(n-1)/2,使用大O表示法得到T1(n)=O(f(n))=O(n2)。第二個(gè)循環(huán)是單層循環(huán),其關(guān)鍵操作是循環(huán)內(nèi)得if語(yǔ)句(程序第10行),它的執(zhí)行頻度為n,使用大O表示法得到T2(n)=O(g(n))=O(n)。整個(gè)程序的漸進(jìn)時(shí)間復(fù)雜度按照加法規(guī)則為T(n)=O(max{f(n),g(n)})=O(n2)。
類似地,如果一個(gè)程序的循環(huán)中有一個(gè)包含有循環(huán)的函數(shù)調(diào)用語(yǔ)句,也可以在被調(diào)用的函數(shù)內(nèi)部尋找關(guān)鍵操作,使用這個(gè)規(guī)則來計(jì)算其漸進(jìn)時(shí)間復(fù)雜度。
在大O表示法的乘法規(guī)則里有一個(gè)特例,如果T1(n)=O?,c是一個(gè)與n無關(guān)的任意常數(shù),T2(n)=O(f(n)),則有
T(n)=T1(n)T2(n)=O(cf(n))=O(f(n))
這也說明在大O表示法中,任何非0正常數(shù)都屬于同一數(shù)量級(jí),記為O(1)。
事實(shí)上,要全面分析一個(gè)算法,需要考慮算法在最壞環(huán)境下的時(shí)間代價(jià),在最好情況下的時(shí)間代價(jià)和在平均情況下的時(shí)間代價(jià)。而大O表示法時(shí)針對(duì)最壞情況而言的。
當(dāng)n充分大時(shí),各種函數(shù)的增長(zhǎng)有如下關(guān)系:
c < log2n < n < nlog2n < n2 < n3 < 2^n < 3^n < n!
其中,c是與n無關(guān)的任意正數(shù)。如果一個(gè)算法的時(shí)間復(fù)雜度取到c、log2n、n或nlog2n,那么他的時(shí)間效率比較高,如下表所示。如果取到2^n, 3 ^n或n!,那么當(dāng)n稍大一點(diǎn),算法的時(shí)間代價(jià)就會(huì)變得很大,以至于不能計(jì)算了。
漸進(jìn)的空間復(fù)雜度
當(dāng)問題規(guī)模n充分大時(shí),需要的存儲(chǔ)空間隨之變化的情況也可以像分析時(shí)間復(fù)雜度一樣用大O表示法來表示。設(shè)S(n)是算法的漸進(jìn)空間復(fù)雜度,在最壞的情況下它可以表示為問題規(guī)模n的某個(gè)函數(shù)f(n)的數(shù)量級(jí),記為
S(n)=O(f(n))
同樣需要注意的是,存儲(chǔ)空間指的是為解決問題所需要的輔助存儲(chǔ)空間。例如在排序算法中為移動(dòng)數(shù)據(jù)所需的歸時(shí)工作單元,在遞歸算法中所需的遞歸工作棧等。
通常,只有完成同一功能的幾個(gè)算法之間才具有可比性。例如,同樣是排序算法,待排序數(shù)據(jù)都是n個(gè),作為輸入和存放這些數(shù)據(jù)的數(shù)組或鏈表結(jié)點(diǎn)也同樣都是n個(gè),因此這些輸入數(shù)據(jù)所占用的存儲(chǔ)空間不用進(jìn)行比較,可比較的只有那些輔助的或附加的存儲(chǔ)空間。可以使用大O表示法來標(biāo)記這些空間,用以比較各算法的優(yōu)劣。
總結(jié)
- 上一篇: Oracle Coherence运维监控
- 下一篇: 2013-1-30 六级词汇造句