算法(1)--時間和空間複雜度

算法(1)--時間和空間複雜度

初識

算法定義

算法是獨立存在的一種解決問題的方法和思想:python

  • 求解一個問題步驟的描述
  • 是求解問題的方法
  • 它是指令的有限序列
  • 其中每條指令表示一個或者多個操做

對於算法而言,實現的語言並不重要,重要的是思想算法

算法特性

  • 肯定性:無二義
  • 有窮性:合適時間內能夠執行
  • 輸入項
  • 輸出項
  • 可行性:算法的每一步都是可行的


複雜度

時間複雜度

定義

​ 通常狀況下,算法中基本操做重複執行的次數問題規模n的某個函數,用T(n)表示(語句頻度),如有某個輔助函數f(n),使得當n趨近於無窮大時T(n)/f(n)極限值不等於零的常數,則稱f(n)T(n)同數量級函數。記做T(n)=O(f(n)),稱O(f(n))爲算法的漸進時間複雜度(O是數量級的符號 ),簡稱時間複雜度數組

求解步驟

求解算法時間複雜度的步驟:函數

  1. 找出算法中的基本語句,計算基本操做執行次數T(n)oop

    # 基本操做即算法中的每條語句(以;號做爲分割),語句的執行次數也叫作語句的頻度。在作算法分析時,通常默認爲考慮最壞的狀況。
  2. 計算基本語句的執行次數T(n)數量級spa

    # 忽略常量、低次冪和最高次冪的係數,令f(n)=T(n)的數量級
  3. 用大O來表示時間複雜度指針

    # 當n趨近於無窮大時,若是lim(T(n)/f(n))的值爲不等於0的常數,則稱f(n)是T(n)的同數量級函數。記做T(n)=O(f(n)),即爲時間複雜度。

example_1:code

n = 1000  # T(n) = 1
j = 1   # T(n) = 1
num1 = 1   # T(n) = 1
num2 = 2   # T(n) = 1
for i in range(0, n):   # T(n) = n
    num1 += 1   # T(n) = n
    while j < n:  # T(n) = n*log(n), 以2爲底
        j *= 2  # T(n) = n*log(n), 以2爲底
        num2 += 1  # T(n) = n*log(n), 以2爲底
print(num1, num2)  # T(n) = 1
  1. 總的T(n):
    \[ T(n) = 5 + 2n + 3nlog_2n \]blog

  2. 忽略掉T(n)中的常量、低次冪和最高次冪的係數,數量級
    \[ f(n) = nlog_2n \]排序

  3. 求極限
    \[ lim(T(n)/f(n)) = lim((3nlog_2n + 2n + 4)/(nlog_2n) = 3 \]
    因此時間複雜度能夠用大O表示,爲
    \[ O(f(n)) = O(nlog_2n) \]

簡化的計算步驟:

能夠看出,決定算法複雜度的是執行次數最多的語句,這裏是num2 += 1j *= 2通常也是最內循環的語句。而且,一般將求解極限是否爲常量也省略掉?

  1. 找到執行次數最多的語句
  2. 計算語句執行次數的數量級
  3. 大O來表示時間複雜度

example_1

# 1.執行次數最多的語句爲
while  j < n:
    j *= 2
    num2 += 1
T(n) = 3n*log(n) 

# 2.數量級
f(n) = n*log(n)

# 3.求極限及大O表示
T(n) = O(nlog(n))

幾種可能

分析算法,存在的幾種可能:

  • 平均時間複雜度
  • 最壞時間複雜度
  • 最優時間複雜度

一些規則

  • 基本操做,即只有常數項,認爲是O(1)

  • 順序結構,時間複雜度按加法進行計算
    \[ T(n, m) = T1(n) + T2(m) = O(max(f(n), g(m))) \]

  • 循環結構,時間複雜度按乘法進行計算
    \[ T(n, m) = T1(n) * T2(m) = O(f(n)*g(m)) \]

  • 分支結構,時間複雜度取最大值

常見的時間複雜度算法:

執行次函數舉例--總的T(n) 時間複雜度 非正式術語
12 O(1) 常數階
2n + 3 O(n) 線性階
3n^2 + 2n + 1 O(n^2) 平方階
5log(n) + 20 O(log(n)) 對數階
2n + 3nlog(n) + 19 O(nlog(n)) nlog(n)階
6n^3 + 2n^2 + 3n + 4 O(n^3) 立方階
2^n O(2^n) 指數階
n! + nlog(n) + 15 O(n!) 階乘

所消耗的時間從小到大:
\[ O(1) < O(log(n)) < O(n) < O(nlog_2(n)) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n) \]

示例演練

example_2

n = 1000
x = 1

for i in range(0, n):
    x += 1  # T(n) = n

for i in range(0, n):
    for j in range(0, n):
        x += 1  # T(n) = n*n
print(x)

分析:注意:T(n)爲執行次數最多語句的頻度

  • 第一個for loop, T(n) = n; f(n) = n時間複雜度爲O(n)
  • 第二個for loop, T(n) = n^2; f(n) = n^2,時間複雜度爲O(n^2)
  • 整個算法的時間複雜度爲O(n + n^2) = O(n^2)

example_3

def func(n):
    for i in range(n):
        for j in range(i, n):
            print("Hello World j = %s" % j)  # T(n) = (n^2)/2 + n/2

分析:注意:==T(n)爲執行次數最多語句的頻度

  • 直接找到語句頻度最高的語句爲print("Hello World j = %s" % j),

    # 當i爲0時,該語句執行n次
    # 當i爲1時,該語句執行n-1次
    # 。。。
    # 因此該語句的T(n) = n + (n-1) + (n-2) + ... + 1 = (n+1)*n/2 = 0.5n^2 + 0.5n
  • 數量級f(n) = n^2

  • 極限存在,時間複雜度 = O(n^2)


example_4

def func(n):
    if n <= 1:
        return 1
    else:
        return func(n - 1) + func(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)。


空間複雜度

相似於時間複雜度的討論,一個算法的空間複雜度(Space Complexity)S(n)定義爲該算法所耗費的存儲空間它也是問題規模n的函數。漸近空間複雜度也經常簡稱爲空間複雜度。
空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度。一個算法在計算機存儲器上所佔用的存儲空間,包括:

  • 存儲算法自己所佔用的存儲空間
    • 存儲算法自己所佔用的存儲空間與算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的算法
  • 算法的輸入輸出數據所佔用的存儲空間
    • 算法的輸入輸出數據所佔用的存儲空間是由要解決的問題決定的,是經過參數表由調用函數傳遞而來的,它不隨本算法的不一樣而改變
  • 算法在運行過程當中臨時佔用的存儲空間
    • 算法在運行過程當中臨時佔用的存儲空間隨算法的不一樣而異,有的算法只須要佔用少許的臨時工做單元,並且不隨問題規模的大小而改變,咱們稱這種算法是「就地"進行的,是節省存儲的算法;有的算法須要佔用的臨時工做單元數與解決問題的規模n有關,它隨着n的增大而增大,當n較大時,將佔用較多的存儲單元,例如快速排序和歸併排序算法就屬於這種狀況

常見算法空間複雜度:

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