在這一章,我們將討論:
我們將使用下面四個定義:
如果存在正常數c和n0使得當n >= n0時t(n) =< cf(n), 則記為t(n) = o(f(n))這些定義的目的是要在函式之間建立一種相對應的級別。我們將比較它們的相對增長率(relative rate of growth)如果存在正常數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))
我們來看這四個定義:
當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'……。我們先用插值排序來計算它的所需時間
假設給定陣列:8,2,4,9,3,6void insertsort (int *arr, int size)
while(loc > 0 && arr[loc - 1] > temp);
arr[loc] = temp;
} }
則第一次排序: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站上郝斌老師的課程。演算法是解決特定問題求解步驟的描述,在計算機中表現為指令的優先序列,並且每條指令表示乙個或多個操作。只聽資料結構課程,當然可以,但是聽完後你可能沒有什麼感覺,因為你不知道他是幹嘛的。但是如果配合演算法來講解,你就...