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

演算法的時間和空間複雜度

演算法定義

   演算法由控制結構(順序、分支和迴圈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)) 為演算法的漸進時間複雜度,簡稱時間複雜度。此外,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)<O(logn)<O(n)<O(nlogn)<O(n2n2)<O(n3n3)<O(2n2n)<O(n!)

數量級 能承受的大致規模 常見演算法
O(1) 任意 直接輸出結果
O(logn) 任意 二分查詢、快速冪
O(n) 以百萬計(五六百萬) 貪心演算法、掃描和遍歷
O(nlogn) 以十萬計(三四十萬) 帶有分治思想的演算法,如二分法
O(n2) 以千計數(兩千) 列舉、動態規劃
O(n3) 不到兩百 動態規劃
O(2n) 24 搜尋
O(n!) 10 產生全排列
O(nn) 8 暴力法破解密碼

O(1)叫常數時間;O(n)、O(n2)、O(n3)、O(n4)……叫做多項式時間;O(2n)、O(3n)……叫做指數時間。

3、演算法的時間複雜度的具體步驟:

  ⑴ 找出演算法中的基本語句;

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

  ⑵ 計算基本語句的執行次數的數量級;

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

  ⑶ 用大Ο記號表示演算法的時間效能。

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

  如果演算法中包含巢狀的迴圈,則基本語句通常是最內層的迴圈體,如果演算法中包含並列的迴圈,則將並列迴圈的時間複雜度相加。舉個例子:第一個for迴圈的時間複雜度為Ο(n),第二個for迴圈的時間複雜度為Ο(n2),則整個演算法的時間複雜度為Ο(n+n2)=Ο(n2)。

let x =1;
let y = 1;
for (let i=1; i<=n; i++)  {
     x++; 
} 
for (let j=1; j<=m; i++){
     for (let k=1; k<=m; j++)  {
        y++; 
      }    
}  
             

4、常見的時間複雜度進行示例

(1)O(1)

  let x=1;

  (2) O(n)

sum=0;                      (1次)  
for(i=1;i<=n;i++) {         (n+1次)  
     sum++;                 (n+1次)
} 

  

  Θ(2n+2)=n  (Θ即:去低階項,去掉常數項,去掉高階項的常參得到),所以

 

 

let sum=0;                 (1次)  
for(let i=1;i<=n;i++)      (n+1次)  
   for(let j=1;j<=n;j++)   ((n+1)(n+1)次)  
    sum++;                ((n+1)(n+1)次) 

  

  Θ(2n2+5n+3)=n2 T(n)=O(n2);

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

  (4)O(log2n)  

  

i = 1;
while(i<=n){
   i = i*2;
}

  

  

   i = 2, 4, 8, 16 ,32,64,128  => i = 2x, 假設x次(時間頻度)後跳出迴圈,因為 i<=n ,所以2x <= n => x = log2n

    所以 T(n) = O( log2n)

while(n){
   n = n/2;
}

  

  128,64,32,16,8,4,2,...

   => 2x  = n => x = log2n

   (5)O(n3) 

   

for(i=0;i<n;i++)  
   {    
      for(j=0;j<i;j++)    
      {  
         for(k=0;k<j;k++)  
            x=x+2;    
      }  
   }  

  O(n3)