1. 程式人生 > >算法的時間復雜度和空間復雜度

算法的時間復雜度和空間復雜度

常量 它的 並且 alt 綜合 nom 交換 等於 for

原文https://blog.csdn.net/zolalad/article/details/11848739

博主zolalad

  通常,對於一個給定的算法,我們要做 兩項分析。第一是從數學上證明算法的正確性,這一步主要用到形式化證明的方法及相關推理模式,如循環不變式、數學歸納法等。而在證明算法是正確的基礎上,第二部就是分析算法的時間復雜度。算法的時間復雜度反映了程序執行時間隨輸入規模增長而增長的量級,在很大程度上能很好反映出算法的優劣與否。因此,作為程序員,掌握基本的算法時間復雜度分析方法是很有必要的。
算法執行時間需通過依據該算法編制的程序在計算機上運行時所消耗的時間來度量。

一、度量一個程序的執行時間通常有兩種方法

 1)事後統計的方法

  • 程序完成後,依據統計方法對算法進行估算;
  • 兩個缺陷:
  1. 要想對設計的算法的運行性能進行評測,必須先依據算法編制相應的程序並實際運行;
  2. 所得時間的統計量依賴於計算機的硬件、軟件等環境因素,有時容易掩蓋算法本身的優勢。

 2)事前分析估算的方法

  • 在編寫程序前,依據統計方法對算法進行估算;
  • 因事後統計方法更多的依賴於計算機的硬件、軟件等環境因素,有時容易掩蓋算法本身的優劣。因此人們常常采用事前分析估算的方法。

 3)一個用高級語言編寫的程序在計算機上運行時所消耗的時間取決於下列因素:

  1. 算法采用的策略、方法;
  2. 編譯產生的代碼質量;
  3. 問題的輸入規模;
  4. 機器執行指令的速度。

 4)其它

  • 一個算法是由控制結構(順序、分支和循環3種)和原操作(指固有數據類型的操作)構成的,則算法時間取決於兩者的綜合效果。為了便於比較同一個問題的不同算法,通常的做法是,從算法中選取一種對於所研究的問題(或算法類型)來說是基本操作的原操作,以該基本操作的重復執行的次數作為算法的時間量度。

二、時間復雜度

 1)時間頻度

  • 一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但我們不可能也沒有必要對每個算法都上機測試,只需知道哪個算法花費的時間多,哪個算法花費的時間少就可以了。並且一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。
  • 一個算法中的語句執行次數稱為語句頻度或時間頻度,記為T(n);

 2)時間復雜度

  • 在時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它變化時呈現什麽規律。為此,我們引入時間復雜度概念。
  • 一般情況下,算法中基本操作重復執行的次數是問題規模n的某個函數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函數。記作T(n)=O(f(n)),稱O(f(n)) 為算法的漸進時間復雜度,簡稱時間復雜度。

 3)其它

  上面公式中用到的 Landau符號是由德國數論學家保羅·巴赫曼(Paul Bachmann)在其1892年的著作《解析數論》首先引入,由另一位德國數論學家艾德蒙·朗道(Edmund Landau)推廣。Landau符號的作用在於用簡單的函數來描述復雜函數行為,給出一個上或下(確)界。在計算算法復雜度時一般只用到大O符號,Landau符號體系中的小o符號、Θ符號等等比較不常用。這裏的O,最初是用大寫希臘字母,但現在都用大寫英語字母O;小o符號也是用小寫英語字母o,Θ符號則維持大寫希臘字母Θ。
T (n) = Ο(f (n)) 表示存在一個常數C,使得在當n趨於正無窮時總有 T (n) ≤ C * f(n)。簡單來說,就是T(n)在n趨於正無窮時最大也就跟f(n)差不多大。也就是說當n趨於正無窮時T (n)的上界是C * f(n)。其雖然對f(n)沒有規定,但是一般都是取盡可能簡單的函數。例如,O(2n2+n +1) = O (3n2+n+3) = O (7n2 + n) = O ( n2 ) ,一般都只用O(n2)表示就可以了。註意到大O符號裏隱藏著一個常數C,所以f(n)裏一般不加系數。如果把T(n)當做一棵樹,那麽O(f(n))所表達的就是樹幹,只關心其中的主幹,其他的細枝末節全都拋棄不管。
在各種不同算法中,若算法中語句執行次數為一個常數,則時間復雜度為O(1),另外,在時間頻度不相同時,時間復雜度有可能相同,如T(n)=n2+3n+4與T(n)=4n2+2n+1它們的頻度不同,但時間復雜度相同,都為O(n2)。 按數量級遞增排列,常見的時間復雜度有:常數階O(1),對數階O(log2n),線性階O(n), 線性對數階O(nlog2n),平方階O(n2),立方階O(n3),..., k次方階O(nk),指數階O(2n)。隨著問題規模n的不斷增大,上述時間復雜度不斷增大,算法的執行效率越低。技術分享圖片

  • 從圖中可見,我們應該盡可能選用多項式階O(nk)的算法,而不希望用指數階的算法。
  • 常見的算法時間復雜度由小到大依次為:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
  • 一般情況下,對一個問題(或一類算法)只需選擇一種基本操作來討論算法的時間復雜度即可,有時也需要同時考慮幾種基本操作,甚至可以對不同的操作賦予不同的權值,以反映執行不同操作所需的相對時間,這種做法便於綜合比較解決同一問題的兩種完全不同的算法。

三、求解算法的時間復雜度

 1)步驟

  1、找出算法中的基本語句:

   算法中執行次數最多的那條語句就是基本語句,通常是最內層循環的循環體。

  2、計算基本語句的執行次數的數量級:

  • 只需計算基本語句執行次數的數量級,這就意味著只要保證基本語句執行次數的函數中的最高次冪正確即可,可以忽略所有低次冪和最高次冪的系數。這樣能夠簡化算法分析,並且使註意力集中在最重要的一點上:增長率。

  3、用大Ο記號表示算法的時間性能。

  • 將基本語句執行次數的數量級放入大Ο記號中。

 2)其它

  • 如果算法中包含嵌套的循環,則基本語句通常是最內層的循環體,如果算法中包含並列的循環,則將並列循環的時間復雜度相加。例如:
  1. for (i=1; i<=n; i++)
  2. x++;
  3. for (i=1; i<=n; i++)
  4.  for (j=1; j<=n; j++)
  5. x++;
  1. 第一個for循環的時間復雜度為Ο(n);
  2. 第二個for循環的時間復雜度為Ο(n2);
  3. 整個算法的時間復雜度為Ο(n+n2)=Ο(n2)。
  • Ο(1)表示基本語句的執行次數是一個常數,一般來說,只要算法中不存在循環語句,其時間復雜度就是Ο(1)。其中Ο(log2n)、Ο(n)、 Ο(nlog2n)、Ο(n2)和Ο(n3)稱為多項式時間,而Ο(2n)和Ο(n!)稱為指數時間。計算機科學家普遍認為前者(即多項式時間復雜度的算法)是有效算法,把這類問題稱為P(Polynomial,多項式)類問題,而把後者(即指數時間復雜度的算法)稱為NP(Non-Deterministic Polynomial, 非確定多項式)問題。
  • 一般來說多項式級的復雜度是可以接受的,很多問題都有多項式級的解——也就是說,這樣的問題,對於一個規模是n的輸入,在n^k的時間內得到結果,稱為P問題。有些問題要復雜些,沒有多項式時間的解,但是可以在多項式時間裏驗證某個猜測是不是正確。比如問4294967297是不是質數?如果要直接入手的話,那麽要把小於4294967297的平方根的所有素數都拿出來,看看能不能整除。還好歐拉告訴我們,這個數等於641和6700417的乘積,不是素數,很好驗證的,順便麻煩轉告費馬他的猜想不成立。大數分解、Hamilton回路之類的問題,都是可以多項式時間內驗證一個“解”是否正確,這類問題叫做NP問題。

四、計算算法時間復雜度時的程序分析法則

 1)對於一些簡單的輸入輸出語句或賦值語句,近似認為需要O(1)時間;

 2)對於順序結構,需要依次執行一系列語句所用的時間可采用大O下"求和法則";

  • 求和法則:是指若算法的2個部分時間復雜度分別為 T1(n)=O(f(n))和 T2(n)=O(g(n)),則 T1(n)+T2(n)=O(max(f(n), g(n)))
  • 特別地,若T1(m)=O(f(m)), T2(n)=O(g(n)),則 T1(m)+T2(n)=O(f(m) + g(n))

 3)對於選擇結構,如if語句,它的主要時間耗費是在執行then字句或else字句所用的時間,需註意的是檢驗條件也需要O(1)時間

 4)對於循環結構,循環語句的運行時間主要體現在多次叠代中執行循環體以及檢驗循環條件的時間耗費,一般可用大O下"乘法法則"

  • 乘法法則:是指若算法的2個部分時間復雜度分別為 T1(n)=O(f(n))和 T2(n)=O(g(n)),則 T1*T2=O(f(n)*g(n))

 5)對於復雜的算法,可以將它分成幾個容易估算的部分,然後利用求和法則和乘法法則技術整個算法的時間復雜度

 6)另外還有以下2個運算法則:

  1. 若g(n)=O(f(n)),則O(f(n))+ O(g(n))= O(f(n));
  2. O(Cf(n)) = O(f(n)),其中C是一個正常數

五、示例

 1)O(1)

Temp=i
i = j
j = temp    
  • 以上三條單個語句的頻度均為1,該程序段的執行時間是一個與問題規模n無關的常數。算法的時間復雜度為常數階,記作T(n)=O(1)。註意:如果算法的執行時間不隨著問題規模n的增加而增長,即使算法中有上千條語句,其執行時間也不過是一個較大的常數。此類算法的時間復雜度是O(1)。

 2)O(n2)

  1、交換i和j的內容
  1. sum=0; (一次)
  2. for(i=1;i<=n;i++) (n+1次)
  3. for(j=1;j<=n;j++) (n2次)
  4. sum++; (n2次)
  • 解:因為Θ(2n2+n+1)=n2(Θ即:去低階項,去掉常數項,去掉高階項的常參得到),所以T(n)= =O(n2);
  2、 例1
  1. for (i=1;i<n;i++)
  2. {
  3. y=y+1; ①
  4. for (j=0;j<=(2*n);j++)
  5. x++; ②
  6. }
  • 解: 語句1的頻度是n-1
  • 語句2的頻度是:
    (n-1)*(2n+1)=2n2-n-1
    f(n)=2n2-n-1+(n-1)=2n2-2;
    Θ(2n2-2)=n2

# 該程序的時間復雜度T(n)=O(n2).

  • 一般情況下,對步進循環語句只需考慮循環體中語句的執行次數,忽略該語句中步長加1、終值判別、控制轉移等成分,當有若幹個循環語句時,算法的時間復雜度是由嵌套層數最多的循環語句中最內層語句的頻度f(n)決定的。

 3)O(n)

  1. a=0;
  2. b=1; ①
  3. for (i=1;i<=n;i++) ②
  4. {
  5. s=a+b;    ③
  6. b=a;     ④
  7. a=s;     ⑤
  8. }

解: 語句1的頻度:2,
語句2的頻度: n,
語句3的頻度: n-1,
語句4的頻度:n-1,
語句5的頻度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n).

 

 4)O(log2n)

  1. i=1; ①
  2. hile (i<=n)
  3. i=i*2; ②

 解: 語句1的頻度是1,
設語句2的頻度是f(n), 則:2^f(n)<=n;f(n)<=log2n
取最大值f(n)=log2n,
T(n)=O(log2n )

 5)O(n3)

  1. for(i=0;i<n;i++)
  2. {
  3. for(j=0;j<i;j++)
  4. {
  5. for(k=0;k<j;k++)
  6. x=x+2;
  7. }
  8. }
  • 解:當i=m, j=k的時候,內層循環的次數為k當i=m時, j 可以取 0,1,...,m-1 , 所以這裏最內循環共進行了0+1+...+m-1=(m-1)m/2次所以,i從0取到n, 則循環共進行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以時間復雜度為O(n3).

六、常用的算法的時間復雜度和空間復雜度

技術分享圖片

  • 一個經驗規則:其中c是一個常量,如果一個算法的復雜度為c 、 log2n 、n 、 n*log2n ,那麽這個算法時間效率比較高 ,如果是2n ,3n ,n!,那麽稍微大一些的n就會令這個算法不能動了,居於中間的幾個則差強人意。
  • 算法時間復雜度分析是一個很重要的問題,任何一個程序員都應該熟練掌握其概念和基本方法,而且要善於從數學層面上探尋其本質,才能準確理解其內涵。

七、算法的空間復雜度

  • 類似於時間復雜度的討論,一個算法的空間復雜度(Space Complexity)S(n)定義為該算法所耗費的存儲空間,它也是問題規模n的函數。漸近空間復雜度也常常簡稱為空間復雜度。

  空間復雜度(Space Complexity)是對一個算法在運行過程中臨時占用存儲空間大小的量度。一個算法在計算機存儲器上所占用的存儲空間,包括存儲算法本身所占用的存儲空間,算法的輸入輸出數據所占用的存儲空間和算法在運行過程中臨時占用的存儲空間這三個方面。算法的輸入輸出數據所占用的存儲空間是由要解決的問題決定的,是通過參數表由調用函數傳遞而來的,它不隨本算法的不同而改變。存儲算法本身所占用的存儲空間與算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的算法。算法在運行過程中臨時占用的存儲空間隨算法的不同而異,有的算法只需要占用少量的臨時工作單元,而且不隨問題規模的大小而改變,我們稱這種算法是“就地\"進行的,是節省存儲的算法,如這一節介紹過的幾個算法都是如此;有的算法需要占用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將占用較多的存儲單元,例如將在第九章介紹的快速排序和歸並排序算法就屬於這種情況。

  如當一個算法的空間復雜度為一個常量,即不隨被處理數據量n的大小而改變時,可表示為O(1);當一個算法的空間復雜度與以2為底的n的對數成正比時,可表示為0(10g2n);當一個算法的空I司復雜度與n成線性比例關系時,可表示為0(n).若形參為數組,則只需要為它分配一個存儲由實參傳送來的一個地址指針的空間,即一個機器字長空間;若形參為引用方式,則也只需要為其分配存儲一個地址的空間,用它來存儲對應實參變量的地址,以便由系統自動引用實參變量。

算法的時間復雜度和空間復雜度