第二章 演算法分析(1)

2021-09-20 03:35:55 字數 3750 閱讀 7812

在這一章,我們將討論:

我們將使用下面四個定義:

如果存在正常數c和n0使得當n >= n0時t(n) =< cf(n), 則記為t(n) = o(f(n))

如果存在正常數c和n0使得當n >= n0時t(n) >= cg(n), 則記為t(n) = omega(f(n))

t(n) = (h(n))當且僅當t(n) = o(h(n))和t(n) = (h(n))

如果對所有的常數c存在n0使得當n > n0時t(n)< cp(n),則記為t(n) = o(p(n))

這些定義的目的是要在函式之間建立一種相對應的級別。我們將比較它們的相對增長率(relative rate of growth)

我們來看這四個定義:

當n較小的時候,1000n是要大於n^2的,但n^2的增長率則更快,所以總有乙個數n0,使得n^2 > 1000n, 在我們的例子中,t(n) = 1000n, f(n) = n^2.

如果我們用傳統的不等式來比較增長率,

第乙個定義就是說t(n)的增長率小於等於f(n)的增長率,

第二個定義t(n)= omega(g(n))就是說t(n)的增長率大於等於g(n)的增長率

第三個定義t(n) = (h(n))是說t(n)的增長率等於h(n)的增長率。

第四個定義t(n) = o(p(n))是說t(n)的增長率小於p(n)的增長率,它不同於o,因為o

我們舉乙個例子,一組數從a1到an,然後按照需求進行排序,排序後每個數字只出現一次,使得a1'< a2'< a3'……。我們先用插值排序來計算它的所需時間

void insertsort (int *arr, int size)  

while(loc > 0 && arr[loc - 1] > temp);

arr[loc] = temp;

} }

假設給定陣列:8,2,4,9,3,6

則第一次排序:2,8,4,9,3,6

則第二次排序:2,4,8,9,3,6

則第三次排序:2,4,8,9,3,6

則第四次排序:2,3,4,8,9,6

則第四次排序:2,3,4,6,8,9

一般來說我們想知道演算法執行時間的上限,這樣方便使用者進行更好的操作。演算法的執行時間取決於好多因素,其中乙個因素是輸入本身,另乙個因素是資料規模。通常我們處理輸入的方式是將輸入引數化,我們會把執行時間看作對待排列資料規模的。

所以我們t(n)定義為輸入規模為n時的最長執行時間。

『我們有時候也討論程式所需要的平均時間,這裡t(n)就成了輸入規模n之下所有可能輸入的期望時間,什麼是期望時間呢?每種輸入執行的時間乘以那種輸入出現的概率就是期望時間。』

演算法執行時間的上限還取決於計算機的執行速度。所以當我們比較演算法時,我們比較的是演算法間的相對速度。忽略掉那些依賴於機器的常量,不去檢驗實際執行時間,而關注執行時間的增長。

所以說在不同的機器上跑同樣的演算法,最長情況時間總是不一樣的,我們得想方設法去除機器效能的影響,而只是去單單分析演算法本身的優劣,以便於在具體量化演算法效能的時候又能去除機器因素的影響,因為我們講演算法分析而不是計算機系統分析。於是有人發明了θ符號。對於這個符號我們來看幾個例子,再看定義

我們需要掌握幾個漸進符號

θ:θ符號掌握起來很簡單,你要做的就是寫個公式,拋棄他的低階項,忽略前面的常數因子。

例如:如果公式為 $$a x ^2 + b x + c = f(x)$$

那麼有$$θ(x^2)= f(x)$$

如果n趨向於無窮大的時候,總會有$$thea(x) < thea(x^2)$$

也就是說它相當於取出f(x)的最高次項然後去掉其常數因子,然後放入θ()中,而放入θ()中又表示了什麼呢?為了弄清楚這個問題,我們來看其定義:

θ(g(n)) = f(n) : 存在某個常數 c1, c2, 和n0,使得當 n > = n0時,有 0 < = c1 g(n) < = f(n) < = c2 g(n)

從後面半句開始,

存在 n > n0, 0 < = c1 * g(n)

這裡保證了g(n)是乙個增函式。接著有 $$c1 g(n) <= f(n) <= c2 g(n)$$

在腦海裡可以想象出來函式影象 [ c1 g(n),c2 g(n) ]這個區間實際上是搖擺的曲線.如果把g(n)看做是y = x這樣的正比例函式,就是乙個扇形是吧?一根曲線搖擺著,掃出乙個扇形來。而f(n)就是這組成扇形的眾多曲線中的一根!這裡也就是說,無論g(n)是什麼,總能有乙個常數因子使得 $$c1 * f(n) =g(n).$$

再返回來原來的題目中去理解:

$$a x ^2 + b x + c = f(x)$$

那麼有$$θ(x^2)= f(x)$$

這裡θ(x^2)表示,在x趨於無窮大這個過程中,有乙個常數c使得 $$c n ^2 = a x ^2 + b * x + c = f(x)$$

也就是:同!階!數!(見高數上).

這樣,我們用θ()來描述先程式的效能n^2 + n變成了θ(n ^ 2),這裡表明:

在n趨於無窮大的過程中,總有乙個常數因子使得

$$c * n^2 = n ^2 + n$$

這個常數因子c在上文中說到,由機器和其它非演算法因素來決定。也就是說,我們用θ(n ^ 2)來表示乙個演算法的效能的時候,我們就完全忽略了機器,只研究演算法本身,它好像是說:無論你在什麼機器上跑這個演算法,只要輸入規模足夠大,你的效能都是n ^ 2的整數倍,至於是多少倍,由機器決定的,反正在同樣的機器上,這個倍數是相等了。

對於每乙個j的取值,迴圈會做多少次操作呢? 在漸近上,這等於j乘上某個常數,所以應該是thea(j)

看看迴圈次數計算一下時間 $$t(n)=\sum_^nthea(j) = thea(n^2)$$

(因為(求和:fout從1到size)就等於1+2+3+……算數級數求和)

插值排序對於n很小的時候很快,但對於n很大的時候就時間很長了

所以我們引入一種新的排序方式做對比——歸併排序

歸併排序我們做的就是遞迴地對a[1到n/2]這部分,以及a[n/2+1到n]這部分排序。

所以我們的輸入是分為兩部分的。第三步,我們把排序好的兩個表歸併,對於歸併排序,每一步我們只需要關注兩組數中元素大小,

所以對於總數為n的輸入,時間是$$t(n)=2t(n/2)+thea(n)=thea(n*lgn)$$

#include #include #include void merge(int *r,int low,int m,int high);    // 這個函式用來將兩個排好序的陣列進行合併

void mergesort(int r,int low,int high); //這個函式用來將問題細分

int main(void)

//輸入10個數,並判斷是否為正整數

}mergesort(a,low,high);

for(i = low;i <= high;i++)

printf("%d ",a[i]);

printf("\n");

return 0;

}void merge(int *r,int low,int m,int high)

void mergesort(int r,int low,int high)

}

第二章 詞法分析1

前端處理的是和源語言相關,後端處理的是和體系結構和目標機相關的程式。讀入字元流後,詞法分析器對整個字元流進行切分,按照它們是關鍵字 識別符號 標點符號 字串 整型數做乙個明確的劃分。每乙個if lparen等都是記號 單詞 結束會有乙個eof。不同的語言,列舉型別是有限的但不一定相同。lexeme是...

總論 第二章 演算法分析

演算法分析 演算法分析主要集中在時間複雜度和空間複雜度 這裡也只是有所了解,演算法中常見的就那幾個級別,具體還是要看後面的演算法再了解 空間複雜度 t n o f n 表示t n de增長率小於等於f n 也是在演算法分析中最主要使用的表達方式。f n 表示的是演算法t n 增長率的乙個上界,他不需...

第二章 演算法

本章內容了解即可。如果大家對資料結構完全不了解,我建議你先去看一下b站上郝斌老師的課程。演算法是解決特定問題求解步驟的描述,在計算機中表現為指令的優先序列,並且每條指令表示乙個或多個操作。只聽資料結構課程,當然可以,但是聽完後你可能沒有什麼感覺,因為你不知道他是幹嘛的。但是如果配合演算法來講解,你就...