1. 程式人生 > >時間複雜度與大O表示法

時間複雜度與大O表示法

相同的程式碼 對於每臺機器執行的總時間不同,但是執行的基本運算數量大體相同
我們假定計算機執行演算法每一個操作的時間都是固定的一個時間單位,那麼有多少個基本操作就會花費多少個時間單位。演算法對於不同的機器環境而言,確切的說單位時間是不同的,但是對於演算法進行多少個基本操作(就是花費多少時間單位)在規模數量級上卻是相同的,由此可以忽略機器環境的影響而客觀的反應演算法的時間效率。

  • 演算法的時間複雜度

*用來度量演算法的執行時間,記作: T(n) = O(f(n))。它表示隨著 輸入大小n 的增大,演算法執行需要的時間的增長速度可以用 f(n) 來描述。

  • a+b+c=1000; a^2 + b^2 = c^2 求出a,b,c abc是自然數

    //第一種 計算的步驟多
    public static void demo(){
    for(int a = 0 ; a < 1000; a++){//執行1000次
    for(int b = 0 ; b < 1000; b++){//執行1000次
    for(int c = 0; c < 1000; c++){//執行1000次
    if((a + b + c == 1000) && ((aa)+(bb) == c*c)){//執行2或10次 不需要特別關心
    System.out.println(a + “—” + b + “—” + c);
    }
    }
    }
    }
    }

基本步驟的數量: T = 1000 * 1000 * 1000 * 2
如果將1000改為2000 那麼 T = 2000 * 2000 * 2000 * 2
1000和2000都代表規模的大小, T = N * N * N * 2
最終的時間複雜度是跟規模有關係 T(n) = n^3 * 2 or T(n) = n^3 * 10
我們可以這樣認為 T(n) = n^3 跟以上是同等數量級上,並且它的走勢是一樣的,也就是將常數項省略,因為函式的階數對函式的增長速度的影響是最顯著的
綜合起來:如果一個演算法的執行次數是 T(n),那麼只保留最高次項,同時忽略最高項的係數後得到函式 f(n),此時演算法的時間複雜度就是 O(f(n))


綜合起來:如果一個演算法的執行次數是 T(n),那麼只保留最高次項,同時忽略最高項的係數後得到函式 f(n),此時演算法的時間複雜度就是T(n)。

  • 如何理解"大O記法"
    對於單調的整數函式f, 如果存在一個整數函式g和實常數c>0,使得對於充分大的n總有f(n) <= c*g(n),就是說函式g是f的一個漸近函式(忽略常數),記作f(n) = O(g(n))。
    時間複雜度:假設存在函式g,使得演算法A處理規模為n的問題示例所用時間為T(n) = O(g(n)),則稱O(g(n))為演算法A的漸近時間複雜度,簡稱時間複雜度,記為T(n).

  • 對於順序執行的語句或者演算法,總的時間複雜度等於其中最大的時間複雜度。

     void aFunc(int n) {
     // 第一部分時間複雜度為 O(n^2)
     for(int i = 0; i < n; i++) {
         for(int j = 0; j < n; j++) {
              printf("Hello, World!");
         }
     }
     // 第二部分時間複雜度為 O(n)
     for(int j = 0; j < n; j++) {
          printf("Hello, World!");
     }
     此時的時間複雜度是max(O(n^2),O(n))  即O(n^2)
    

時間複雜度分析的基本策略是:從內向外分析,從最深層開始分析。如果遇到函式呼叫,要深入函式進行分析。

  • 練習題: 求該方法的時間複雜度

    void aFunc(int n) {
         for (int i = 2; i < n; i++) {
             i *= 2;
              printf("%i\n", i);
         }
     }
    

參考答案:
假設迴圈次數為 t,則迴圈條件滿足 2^t < n。
可以得出,執行次數t = log(2)(n),即 T(n) = log(2)(n),可見時間複雜度為 O(log(2)(n)),即 O(log n)。

long aFunc(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return aFunc(n - 1) + aFunc(n - 2);
    }
}

參考答案:
顯然執行次數,T(0) = T(1) = 1,同時 T(n) = T(n - 1) + T(n - 2) + 1,這裡的 1 是其中的加法算一次執行。
顯然 T(n) = T(n - 1) + T(n - 2) 是一個斐波那契數列,通過歸納證明法可以證明,當 n >= 1 時 T(n) < (5/3)^n,同時當 n > 4 時 T(n) >= (3/2)^n。
所以該方法的時間複雜度可以表示為 O((5/3)^n),簡化後為 O(2^n)。
可見這個方法所需的執行時間是以指數的速度增長的。如果大家感興趣,可以試下分別用 1,10,100 的輸入大小來測試下演算法的執行時間,相信大家會感受到時間複雜度的無窮魅力

  • 分類

常見時間複雜度有(按增長率):
1.常數階O(1)
2.對數階O(logn)
3.線性階O(n)
4.線性對數階O(nlog2n)
5.k方階:O(n^k),一般控制k的大小,否則就和指數階一樣了,這是很可怕的
6.指數階:O(2^n),一般不用,效能太差
在這裡插入圖片描述

在這裡插入圖片描述

  • 時間複雜度的幾條基本計算規則
    1.基本操作:即只有常數項,認為其時間複雜度為O(1)
    2.順序結構:時間複雜度按加發進行運算
    3.迴圈結構:時間複雜度按乘發進行運算
    4.分支結構:時間複雜度取最大值