在比較演算法的效率時,往往是演算法的已耗用時間與資料項目個數關係間比較,例如,為了得到某個結果,在同一資料量下,哪個演算法運行最快,效率最高,而後改變這一資料量,哪種演算法的時間又最快......
大O標記法
在描述演算法已耗用時間時,往往能看到O(N)、O(logN)等,大O中的O的意思就是"order of"(大約是),它是種概念,就比如 大型車、小型車和中型車,忽略具體大小尺寸,來描述汽車。
首先來跟著我分析一段代碼:
樣本 1:
1 int sum(int n)
2 {
3 int partialSum;
4 partialSum = 0;
5 for(int i = 1;i <= n;i++)
6 {
7 partialSum += i * i * i;
8 }
9 return partialSum;
10 }
這個程式是計算 Σni=1i3 即 從1開始到n的整數,計算它們的立方和
聲明不計時間,賦值、計算、比較每次佔一個時間單位,則從第四行開始,首先花費1個時間單位,接著進入迴圈,給i賦值,又佔一個單位,它只運行一次,接著比較,運行n+1次,i++為兩次計算,一次加一,一次賦值,而它要運行n次,佔2n個時間單位,迴圈主體是四個時間單位(兩個乘法,一個加法,一個賦值),總共執行n次,故花費4n個單位,最後返回花費1個時間單位。
最終,這個程式總共花費1(賦值)+1(賦值)+(1+2n)(比較)+4n(計算)+1(返回)=6n+4 個時間單位,但是,如果每個程式都像這樣計算的話,豈不是讓人崩潰,大O標記法的宗旨就是,捨去所有常量,把它歸入cpu,平台等因素造成的時間,假設這些常量總體為K,則上面那個程式所花費的時間是n+K,按照這種計算方式,每個程式的已耗用時間都會轉化為xn+K,所以我們可以把K捨去。
最終,以大O標記法,這個程式的已耗用時間是O(N)。
如果是一個嵌套迴圈呢,比如:
樣本 2:
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)
{
代碼;
}
}
這段代碼的已耗用時間就為O(N2)了 (自己按照上面那種方法掐指算一下)。
如果把執行個體2添加進樣本1中的返回語句前,則已耗用時間為n+n2,以大O標記法則為O(n2)。
看到了吧,大O標記法是沒有加號的,所有這些都被忽略了,沒有常數,只有迭代次數。
很多與我一樣數學底子不好的同學,遇到O(logN)等這樣帶對數的時候就會迷茫,其實也不難,拿二分搜尋舉例,它的已耗用時間是O(logN)。
什麼是二分搜尋?我來舉個例子,李詠大哥讓你猜個電器的價格,猜中了就歸你了,如果你聰明的話,就不會亂猜,你會首先隨機猜一個自己認為適合的價格(比如一個電視,你猜5000),如果他喊低了,就繼續往上加,但是他喊高了,你會選擇剛才價格的一半(2500),如果他喊高了,則繼續折一半(1250),這次他喊低了,你就猜3750,按照這種原則,你最終猜對了,是3750,你只用了3次。
按照這種方法,最壞的情況下(即猜的次數最多),N個數所用的次數X<=log2N ,因為2x<=n,比如10個數,最多猜測3次,即23<=10 ,3<=log210 。
現在大致明白了log形式的已耗用時間了吧。
這裡列出一些已耗用時間計算的一些法則,是從《資料結構與演算法分析C++描述(第三版)》上面摘錄的
法則1:for迴圈 一個for迴圈的已耗用時間至多是該for迴圈內語句(包括測試)的已耗用時間乘以迭代的次數。
法則2:嵌套迴圈 從裡向外分析這些迴圈。在一組嵌套迴圈內部的一條語句總的已耗用時間為該語句的已耗用時間乘以該組所有迴圈的大小的乘積。
法則3:順序語句 將各個語句的已耗用時間求和即可(這意味著,其中的最大值就是所得的已耗用時間)。
法則4:IF/ELSE語句 一個if/else語句的已耗用時間從不超過判斷再加上每個主體中已耗用時間較長者的總的已耗用時間。
最後,總結一下,其實已耗用時間O就是相對增長率,我們所比較的已耗用時間,就是把他們的相對增長率進行比較,這裡的已耗用時間,都是在最壞情況下得出的,因為資料量充滿不確定性,不保守估計的話,會出錯,瞭解了已耗用時間的演算法,對一個演算法的好壞的判斷就有基準了,以後會用到這裡(尤其是排序算