事后統(tǒng)計(jì)方法
事后統(tǒng)計(jì)方法:這種方法主要是通過(guò)設(shè)計(jì)好的測(cè)試程序和數(shù)據(jù),利用計(jì)算機(jī)計(jì)時(shí)器對(duì)不同算法編制的程序的運(yùn)行時(shí)間進(jìn)行比較,從而確定算法效率的高低。
但這種方法顯然是有很大缺陷的:
必須依據(jù)算法事先編制好程序,這通常需要花費(fèi)大量的時(shí)間和精力。如果編制出來(lái)發(fā)現(xiàn)它根本是很糟糕的算法,不是竹籃打水一場(chǎng)空嗎?
事前分析估算方法
事前分析估算方法:在計(jì)算機(jī)程序編寫(xiě)前,依據(jù)統(tǒng)計(jì)方法對(duì)算法進(jìn)行估算。
經(jīng)過(guò)分析,我們發(fā)現(xiàn),一個(gè)用高級(jí)程序語(yǔ)言編寫(xiě)的程序在計(jì)算機(jī)上運(yùn)行時(shí)所消耗的時(shí)間取決于下列因素:
1.編譯產(chǎn)生的代碼質(zhì)量。
2.算法采用的策略、方法。
3.問(wèn)題的輸入規(guī)模。
4.機(jī)器執(zhí)行指令的速度。
由此可見(jiàn),拋開(kāi)這些與計(jì)算機(jī)、軟件有關(guān)的因素,一個(gè)程序的運(yùn)行時(shí)間依賴于算法的好壞和問(wèn)題的輸入規(guī)模。
簡(jiǎn)單算法比較
計(jì)算1+2+3+...+100的值
第一種算法:
int i, sum = 0,n = 100; /*執(zhí)行1次*/
for(i = 1; i < = n; i++) /*執(zhí)行了n+1次*/
{
sumsum = sum + i; /*執(zhí)行n次*/
}
printf("%d", sum); /*執(zhí)行1次*/
第二種算法:
int sum = 0,n = 100; /*執(zhí)行1次*/
sum = (1 + n) * n/2; /*執(zhí)行1次*/
printf("%d", sum); /*執(zhí)行1次*/
- 第一種算法執(zhí)行了1+(n+1)+n+1=2n+3次。
- 第二種算法執(zhí)行了1+1+1=3次。
事實(shí)上兩個(gè)算法的第一條和最后一條語(yǔ)句是一樣的,所以我們關(guān)注的代碼其實(shí)是中間的那部分,我們把循環(huán)看作一個(gè)整體,忽略頭尾循環(huán)判斷的開(kāi)銷(xiāo),那么這兩個(gè)算法其實(shí)就是n次與1次的差距
為什么在這里我們要看成n次與1次的差距,而不是精確到2n+1 與1次的差距?
我們?cè)賮?lái)延伸一下上面這個(gè)例子:
int i, j, x = 0,sum = 0,n = 100; /*執(zhí)行1次*/
for(i = 1; i < = n; i++)
{
for (j = 1; j < = n; j++)
{
x++; /*執(zhí)行n×n次*/
sumsum = sum + x;
}
}
printf("%d", sum); /*執(zhí)行1次*/
這個(gè)例子中,i從1到100,每次都要讓j循環(huán)100次,而當(dāng)中的x++和sum = sum + x;其實(shí)就是1+2+3+…+10000,也就是1002次,所以這個(gè)算法當(dāng)中,循環(huán)部分的代碼整體需要執(zhí)行n2(忽略循環(huán)體頭尾的開(kāi)銷(xiāo))次。顯然這個(gè)算法的執(zhí)行次數(shù)對(duì)于同樣的輸入規(guī)模n = 100,要多于前面兩種算法,這個(gè)算法的執(zhí)行時(shí)間隨著n的增加也將遠(yuǎn)遠(yuǎn)多于前面兩個(gè)。
此時(shí)你會(huì)看到,測(cè)定運(yùn)行時(shí)間最可靠的方法就是計(jì)算對(duì)運(yùn)行時(shí)間有消耗的基本操作的執(zhí)行次數(shù)。運(yùn)行時(shí)間與這個(gè)計(jì)數(shù)成正比。
我們不關(guān)心編寫(xiě)程序所用的程序設(shè)計(jì)語(yǔ)言是什么,也不關(guān)心這些程序?qū)⑴茉谑裁礃拥挠?jì)算機(jī)中,我們只關(guān)心它所實(shí)現(xiàn)的算法。這樣,不計(jì)那些循環(huán)索引的遞增和循環(huán)終止條件、變量聲明、打印結(jié)果等操作,最終,在分析程序的運(yùn)行時(shí)間時(shí),最重要的是把程序看成是獨(dú)立于程序設(shè)計(jì)語(yǔ)言的算法或一系列步驟。
可以從問(wèn)題描述中得到啟示,同樣問(wèn)題的輸入規(guī)模是n,求和算法的第一種,求1+2+…+n需要一段代碼運(yùn)行n次。那么這個(gè)問(wèn)題的輸入規(guī)模使得操作數(shù)量是f(n) = n,顯然運(yùn)行100次的同一段代碼規(guī)模是運(yùn)算10次的10倍。而第二種,無(wú)論n為多少,運(yùn)行次數(shù)都為1,即f(n) = 1;第三種,運(yùn)算100次是運(yùn)算10次的100倍。因?yàn)樗莊(n) = n2。
我們?cè)诜治鲆粋€(gè)算法的運(yùn)行時(shí)間時(shí),重要的是把基本操作的數(shù)量與輸入規(guī)模關(guān)聯(lián)起來(lái),即基本操作的數(shù)量必須表示成輸入規(guī)模的函數(shù)。
函數(shù)的漸近增長(zhǎng)在說(shuō)函數(shù)的漸近增長(zhǎng)的例子前,先說(shuō)說(shuō)概念,
函數(shù)的漸近增長(zhǎng):給定兩個(gè)函數(shù)f(n)和g(n),如果存在一個(gè)整數(shù)N,使得對(duì)于所有的n > N,f(n)總是比g(n)大,那么,我們說(shuō)f(n)的漸近增長(zhǎng)快于g(n)。
文字說(shuō)明,比較難理解,我們利用下面的表格來(lái)說(shuō)明
注意:n^2代表n 的平方,n^3代表n的立方
數(shù)值\函數(shù) | n | 2n | 2n+1 | 3n+8 | n^2 | 2n^2 | 2n^2+2n+1 | n^3 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 3 | 11 | 1 | 2 | 5 | 1 |
2 | 2 | 4 | 5 | 14 | 4 | 8 | 13 | 8 |
3 | 3 | 6 | 7 | 17 | 9 | 18 | 25 | 27 |
5 | 5 | 10 | 11 | 23 | 25 | 50 | 61 | 125 |
9 | 9 | 18 | 19 | 35 | 81 | 162 | 181 | 729 |
10 | 10 | 20 | 21 | 38 | 100 | 200 | 221 | 1000 |
100 | 100 | 200 | 201 | 308 | 10000 | 20000 | 20201 | 1000000 |
1000 | 1000 | 2000 | 2001 | 3008 | 1000000 | 2000000 | 2002001 | 1000000000 |
10000 | 10000 | 20000 | 20001 | 30008 | 100000000 | 200000000 | 200020001 | 1000000000000 |
100000 | 100000 | 200000 | 200001 | 300008 | 10000000000 | 20000000000 | 20000200001 | 1000000000000000 |
例如:f(n)=2n^2+1,g(n)=2n+1
當(dāng)n=1是f(n)=g(n),這個(gè)時(shí)候?qū)?yīng)上面的概念,N=1,當(dāng)n>N,也就是當(dāng)n>1時(shí),f(n)>g(n),所以,我們說(shuō)f(n)的漸近增加快于g(n)
為了更好理解這些特點(diǎn),我做了一些圖表,以便更加清楚的知道為什么
當(dāng)輸入數(shù)值非常大的時(shí)候,兩條曲線基本重疊,或者可以說(shuō)看作重疊,所以得出結(jié)論是,2n+1其中里面作為常數(shù)的1,在輸入數(shù)值大到一定程度,他對(duì)于函數(shù)的影響可以忽略不計(jì),這時(shí)候的1就被看作是次要項(xiàng)
同樣,2n^2+2n+1
由于漸近增長(zhǎng)是可以看作是一種抽象,所以他的對(duì)比具有一些特點(diǎn):
1.注意關(guān)注函數(shù)最高次冪的變化
2.忽略次要項(xiàng)與乘數(shù)
特別感謝:
raylee2007的專(zhuān)欄