算法的時間復雜度分析


算法分析

  • 算法分析即指對一個算法所需要的資源進行預測
    • 內存,通信帶寬或者計算機硬件等資源偶爾是我們關心的
    • 通常,資源是指我們希望測度的計算時間

RAM模型

  • 分析一個算法之前,需要建立一個實現技術的模型,包括描述所用資源及其代價的模型

  • RAM模型:單處理器,隨機存取RAM

    • 指令一條接一條地執行,沒有並發操作(單處理器)
    • 包含真實計算機中的常見指令:算術,數據移動,控制
    • 每條指令所需時間為常量
    • 數據類型為整型和浮點型
  • 灰色領域:真實計算機包含的其他指令,不是常量時間的那種。沒有對存儲器層次進行建模。

算法運行時間

  • 運行時間取決於輸入的內容

    • 相同規模\(n\),不同的序列有不同的運行時間,比如逆序序列或者順序序列
  • 運行時間取決於數據的規模

    • \(n\)越大,時間自然越多
    • 一般來說,算法所需時間與輸入規模同步增長,因此一個程序的運行時間是其輸入的函數
  • 通常我們關心運行時間的上限(最壞情況)

  • 注:我們分析時間時要使用機器獨立的時間單位,即不考慮機器不同帶來的影響。

插入排序時間分析

  • 假設每行每次執行的時間為常量\(c_i\)
for j: 2 to length[A]:
	do key = A[j]
	   i = j-1
       while i>0 and A[i]>key
       		do A[i+1] = A[i]
       		i = i-1
       A[i+1] = key
  1. \(cost:c_1;times:n\) (包含跳出循環的那次)

    注:for循環是剛剛進入循環時就要判斷一次條件,然后再執行j--,再判斷條件,直到判斷條件不滿足,不進入循環。假設循環\(n\)個元素,實際執行\(n+1\) 次比較

  2. \(cost:c_2;times:n-1\)

  3. \(cost:c_3;times:n-1\)

  4. \(cost:c_4;times:\sum\limits_{j=2}^nt_j, t_j\) 為一次for循環中while循環的判斷次數

  5. \(cost:c_5;times:\sum\limits_{j=2}^n(t_j-1),\)

  6. \(cost:c_6;times:\sum\limits_{j=2}^n(t_j-1)\)

  7. \(cost:c_7;times:n-1\)

\(t_j\) 取決於與序列排序情況有關,如果已經排好序了,\(A[j-1]\)總是小於key了,所以每次for循環只算判斷了一次while,總共\(n-1\)次,如果是逆序,前一個總比后一個大,滿足while條件,每次for循環中while判斷次數為\(t_j=j-1+1=j\) ,總共\(\sum\limits_{j=2}^n{t_j}\) 次。

總的運行時間:

\(T(n)=c_1n+c_2(n-1)+c_3(n-1)+c_4\sum\limits_{j=2}^n{t_j}+c_5\sum\limits_{j=2}^n(t_j-1)+c_6\sum\limits_{j=2}^n(t_j-1)+c_7(n-1)\)

漸進分析

  • 如果一個算法的最壞情況運行時間要比另一個算法的低,我們就常常認為它的效率更高。那么如何比較兩個算法的運行時間呢?
  • 漸進表示:忽略每條語句的真實代價,而用常量\(c_i\) 表示,只考慮公式中的最高次項(低階項相對來說不太重要),忽略最高次項的常數系數(對於增長率而言,系數是次要的)
    • 在輸入的規模較小時,由於常數項和低次項的影響,這種看法有時可能是不對的。對規模足夠大的輸入來說,這種看法總是對的。
  • 雖然有時候能夠精確確定一個算法的運行時間,但通常沒有必要。(在RAM模型下,可以精確計算T(n))
  • 漸近分析更有意義(對不是很小的輸入規模而言,從漸進意義上說更有效的算法就是最佳的選擇)

漸進符號

\(\Theta(g(n))=\{f(n):存在正常數c_1,c_2,n_0,對所有的n\ge{n_0},有0\le{c_1g(n)\le{f(n)}\le{c_2g(n)}}\}\)

  • \(\Theta(g(n))\) 是一個集合,記號\(f(n)=\Theta(g(n))\) 是指\(f(n)\) 是這個集合中的一個元素,不是指相等

  • 具體來說:當\(n\)大於某個數時,一個與\(n\)有關的函數\(f(n)\),不管\(n\)如何增長,其大小總是被限制到\(c_1g(n)\)\(c_2g(n)\)之間。

  • 在時間復雜度分析中,\(f(n)\)即我們所要求的\(T(n)\),當我們不需要精確地求出\(T(n)\)時,我們只需要大致知道它隨\(n\)增長時,其值的上下界如何,即這個算法的運行時間肯定不會超過某個時間,不會低於某個時間。

  • **比如:\(T(n)=\Theta(n^2)\) 表示該算法的運行時間不會超過\(c_1n^2\) ,不會低於\(c_2n^2\) **

    • \(\Theta(n^2)\) 是所有滿足該性質的算法的\(T(n)\) 的集合

\(O(g(n))=\{f(n):存在正常數c,n_0,對所有的n\ge{n_0},有0\le{f(n)}\le{cg(n)}\}\)

  • 描述了算法運行的上界,不會超過常數倍的\(g(n)\) ,即最壞情況
  • 比如 \(T(n)=O(n^2)\) 表示該算法運行時間不會超過\(cn^2\)

\(\Omega(g(n))=\{f(n):存在正常數c,n_0,對所有的n\ge{n_0},有0\le{cg(n)}\le{f(n)}\}\)

  • 描述算法運行的下界,表示不低於常數倍\(cg(n)\)

一個漸進正函數中的低階項和最高階項的系數在決定漸進確界(上界、下界)時可以被忽略

分治算法分析

  • 分治法在每一層遞歸上都有三個步驟:

    分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;

    解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題;

    合並:將各個子問題的解合並為原問題的解。

  • 另外,分解到什么規模就夠了呢?即分解到子問題可以找到一個方法,使得在線性時間/常量時間內就可以解決。比如歸並排序問題,排序到什么時候最容易解決呢?當然是分解到序列內只有一個元素

  • 分治法的遞歸式

    • \(T(n)\) 為規模為\(n\)的問題的運行時間,\(D(n)\)為分解問題所需時間,\(C(n)\) 為合並解所需時間

\[T(n)=\begin{cases} \Theta(1) & n\le{c} \\ aT(n/b)+D(n)+C(n) & otherwise \\ \end{cases} \]

  • 使用分治法的歸並排序的遞歸式
    • 第一個式子就是分解到什么規模可以通過\(O(1)\)時間來解決,第二個式子描述的就是子問題的運行時間加上歸並所需要的時間

\[T(n)=\begin{cases} \Theta(1) & n=1 \\ \underbrace{2T(n/2)}_{對兩個子序列排序}+\underbrace{\Theta(n)}_{合並解} & n>1 \\ \end{cases} \]

遞歸式求解

\[T(n)=\begin{cases} \Theta(1) & n=1 \\ 2T(n/2)+\Theta(n) & n>1 \\ \end{cases} \]

注意問題:

  • 假設自變量為整數
  • 忽略邊界條件
  • 忽略上取整,下取整的影響,先假設總能夠被整除,等得到結果后再確定他們是否重要

代換法

  • 猜測解的形式
  • 用數學歸納法找出使解真正有效的常數
  • 僅僅適用於解的形式很容易猜的時候

遞歸樹

  • 將遞歸式轉換成樹形結構,樹中的節點代表在不同遞歸層次付出的代價,利用對和式限界的技術解出遞歸式


主方法

  • 給出遞歸形式\(T(n)=aT(n/b)+f(n)\)的界,其中\(a≥1,b>1,f(n)\)是給定的函數



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM