1. 程式人生 > >前塵----時間復雜度和空間復雜度計算

前塵----時間復雜度和空間復雜度計算

只需要 存在 基礎 n-1 因此 大致 for循環 長時間 計算機

算法:算法是解決特定問題求解步驟的描述,在計算機中表現為指令的有限序列,並且每條指令表示一個或者多個操作。

算法的五個特性: 

  • 輸入輸出:算法具有零個或多個輸入,算法至少有一個或者多個輸出。輸出的形式可以是打印也可以是返回一個或者多個值。
  • 有窮性:指算法在執行有限步驟之後,自動結束而不會出現無限循環,並且每一個步驟在可接受的時間內完成。
  • 確定性:算法的每一步驟都具有確定的含義,不會出現二義性,算法在一定條件下,只有一條執行路徑,相同的輸入只能有唯一的輸出結果。算法的每個步驟被精確定義而無歧義。
  • 可行性:算法的每一步都必須是可行的,也就是說,每一步都能夠通過執行有限次數完成。可行性以為著算法可以轉換為程序上機運行,並得到正確的結果

算法的設計要求: 

  • 正確性:算法的正確性是指算法至少應該具有輸入、輸出和加工處理無歧義性、能正確反映問題的需求、能夠得到問題的正確答案。算法正確性從低到高有4層含義:(1)算法程序沒有語法錯誤;(2)算法程序對於合法的輸入數據能夠產生滿足要求的輸出結果;(3)算法程序對於非法的輸入數據能夠得出滿足規格說明的結果;(4)算法程序對於靜心選擇的,甚至刁難的測試數據都有滿足要求的輸出結果。優良的算法設計通常至少要滿足到第三層。
  • 可讀性:算法設計的另一目的是為了便於閱讀、理解和交流。
  • 健壯性:當輸入數據不合法時,算法也能做出相關處理,而不是產生莫名其妙的結果
  • 時間效率和存儲量低:設計算法應盡量滿足時間效率高和存儲量低的需求

算法效率的度量

  1.事後統計方法:這種方法主要是通過設計好的測試程序和數據,利用計算機計時器對不同的算法編制的程序運行時間進行比較,從而確定算法效率的高低。

  2.事前分析估算方法:在計算機程序編制前,依據統計方法對算法進行估算。

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

  (1)算法采用的策略、方法;

  (2)編譯產生的代碼質量;

  (3)問題的輸入規模;

  (4)機器執行指令的速度

  測定運行時間最可靠的方法就是計算對運行時間有消耗的基本操作的執行次數,運行時間與這個計數成正比。在分心程序的運行時間時,最重要的是把程序看成是獨立於程序設計語言的算法或一系列步驟。

int sum =0;
int n =100;
for(int i = 0; i <n;++i)
{
    sum += i;
}

  像上面這段代碼,其輸入規模為n,那麽時間消耗總量(消耗時間的基本操作的次數,一次基本操作的時間消耗,假設它為O(1),我們把它記為f(n),忽略循環頭的時間消耗。假設求和這樣的基本操作時間消耗為1,那麽在上述算法中,這個求和過程被重復執行了n次,那麽f(n) = n;

  在分析一個算法的運行時間時,重要的是把基本操作的數量與輸入規模關聯起來,即基本操作的數量必須表示成輸入規模的函數。

函數的漸進增長

  函數的漸進增長:給定兩個函數f(n)和g(n),如果存在一個整數N,使得對於所有的n>N,f(n)總是比g(n)大,那麽,我們說f(n)的增長漸進快於g(n)。

  判斷一個算法的效率時,函數中的常數和其它次要項常常可以忽略,而更應該關註主項(最高階項)的階數。如果可以對比幾個算法的關鍵執行次數函數的漸進增長性,基本就可以分析:某個算法,隨著n(輸入規模)增大,它會越來越優於另一算法,或者越來越差於另一算法。這就是事前估算方法的理論依據,通過時間算法復雜度來估算算法的時間效率。

算法的時間復雜度

  算法時間復雜度:在進行算法分析時,語句總的執行次數T(n)是關於問題規模(輸入規模)n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級。算法的時間復雜度,也就是算法的時間量度,記作T(n) = O(f(n))。它表示隨著問題規模n的增大,算法執行時間的增長率和f(n)的增長率相同,稱作算法的漸進時間復雜度,簡稱為時間復雜度。其中f(n)是問題規模n的某個函數。

  這樣用大寫O( )來體現時間復雜度的記法,我們稱之為大O記法。

  一般情況下,隨著n增大,T(n)增長最慢的算法為最優算法。O(1)俗稱為常數階,O(n)俗稱線性階,O(n^2)俗稱為平方階。

推導大O階方法

  1.用常數1取代運行時間中的所有加法常數

  2.在修改後的運行次數函數中,只保留最高階項。

  3.如果最高階項存在且不是1,則去除與這個項相乘的常數,得到的結果就是大O階。

註:順序結構的時間復雜度是O(1),當然前提是這些都是基本操作,例如賦值啊,比較啊等等。

  單純的分支結構(不包含在循環結構裏)的時間復雜度也是O(1),前提也是分支的操作都是基本操作。

  循環結構的時間復雜度就不一定了,單純的循環結構就向上面舉的求和的例子,其時間復雜度就是O(n)。

  要確定某個算法的階次,我們常常需要確定某個特定語句集運行的次數。因此,我們要分析算法的復雜度,關鍵就是要分析循環結構的運行情況。

對數階

int count = 1;
while(count < n)
{
    count = count*2;
    ...    //時間復雜度為O(1)的程序步驟序列
}

那麽這個算法的時間復雜度是多少呢?顯然對這個算法來說其輸入規模的最大值是n,count是每一輪循環的輸入規模,那麽當count漸進增長超過n的時候基本操作語句總共執行了多少次呢?
觀察發現,count每一次循環都是上一次的兩倍。
那麽假設執行的次數為x,輸入規模為變量n,可以得出x和n的關系是:
                    2^x = n;
 計算算法時間復雜度,我們要求的是執行總次數關於輸入問題規模的函數,因此,對上述等式進行變化得:
                  x = log2(n)
    即f(x) = log2(n);
    接下來推導大O記法。
    上述算法的時間復雜度為 O(log(n))。
    在這裏一般會省略log2n中的2,這是因為有換底公式的存在,使得底數具體是多少不需要去關心。

平方階

  平方階一般見於簡單的嵌套for循環

  例如:

for(int i = 0; i < n: ++i)
{
    for(int j =0; j < n:++j)
    {
        /*時間復雜度為O(1)的程序步驟序列*/
    }
}
對於外次for循環的每一次執行,內層for循環都要執行n此,問題的輸入規模為n,那麽執行的總次數就是n^2,即f(n) = n^2
按照大O階的記法,這個算法的時間復雜度為O(n^2)
如果外層的循環變量改為了m。那麽外層的輸入規模為m,內層的輸入規模是n,對外層的每一次執行,內層循環都要執行n此,所以總次數就是n*m
因此其時間復雜度為O(m*n)
因此,循環的時間復雜度等於循環體的復雜度乘以該循環運行的次數。
for(int i =0; i<n; ++i)
{
    for( j = i; j< n; ++j)
    {
        /*算法時間復雜度為O(1)的程序步驟序列*/
    }
}

對這個算法的時間復雜度進行計算,
當i = 0 時,內層循環執行了n次,當i= 1時,內層循環執行了n-1次,當i = 2時內層循循環執行了n-2次……當i等於n-1次時,內層循環執行了1次,那麽總的執行次數就是
    f(n) = n+ (n-1) + (n-2) + (n-3) +... +1 = n(n+1)/2 = n^2/2 + n/2
那麽它的時間復雜度就是O(n^2)

  時間復雜度,難點在於對數列的進行的相關運算,所以計算時間復雜度,要強化基礎數學能力,尤其是數列方面的知識。

包含方法調用的時間復雜度計算

  簡單來說,你把函數調用當做宏定義在原地展開,然後按照上面的示例計算即可。也可以利用大O的加法或乘法法則計算,其實都一樣

  大O的加法法則:f1(n) + f2(n) = O(max(f1(n),f2(n)); f1、f2是為兩個關於屬於規模和基本運算執行次數的函數,加法運算適用於順序結構、if結構、switch結構

  大O的乘法法則:f1(n)*f2(n) = O(f1(n) * f2(n))  適用於嵌套的循環,例如上述例子的嵌套for,外層的時間復雜度為O(n),即運算次數和屬於規模的函數是f1(n) = n;內層循環的時間復雜度也是O(n),其運算次數和輸入規模的函數是f2(n) = n,那麽整個嵌套for循環的時間復雜度就是O(n*n)。

  簡單布爾計算或算數運算以及簡單I/O(內存I/O)的時間復雜度是O(1),這個也是大O表示法的單位時間。至於O(1)具體是多長時間,定義這個量值是沒有意義的,因為,計算時間復雜度,我們只是希望從理論角度出發,大致的估量它可能消耗的時間,程序的具體運行時間消耗還有賴於具體硬件平臺的處理效率。

最壞情況與平均情況

  最壞情況運行時間是一種保證那就是運行時間將不會再壞了。在應用中這是一種最重要的需求。

  平均運行時間是期望算法的運行時間。

  對算法的分析,一種方法是計算所有情況的平均值,這種時間復雜度的計算方法稱為平均時間復雜度。另一種方法是計算最壞情況下的時間復雜度。這種方法稱為最壞平均時間復雜度、

算法的空間復雜度

  算法的空間復雜度:通過計算算法所需的存儲空間實現,算法空間復雜度的計算公式記作:S(n) = O(f(n)),其中n為問題的規模,f(n)為語句關於n所占存儲空間的函數。一般情況下,一個程序在機器上執行時,除了需要存儲程序本身的指令、常數、變量和輸入外,還需要存儲對數據操作的存儲單元。若輸入數據所占空間只取決於問題本身,和算法無關,這樣只需要分析該算法在實現時所需的輔助單元即可。若算法執行時所需的輔助空間相對於輸入數據量而言是個常數,則稱此算法為原地工作,空間復雜度為O(1)。通常,使用“時間復雜度”來指運行時間的需求,使用“空間復雜度”指空間需求。一般說到復雜度,其實都是指“時間復雜度”。

前塵----時間復雜度和空間復雜度計算