運算複雜度理論

**運算複雜度理論**（）係運算理論嘅一個子領域，集中於研究啲（最少理論上）解到嘅運算問題有幾撈絞－即係想分析如果要實際噉用電腦計呢啲問題嘅答案，要消耗幾多資源，當中**資源**包括咗時間同空間（**空間**指記憶體）呀噉。

對運算複雜度嘅研究係電腦科技上嘅重要一環：簡單講，電腦本質上就係以極快速度計大量數嘅機械；但事實表明，有好多運算問題都係理論上解決到、但實際計起上嚟就要用好似宇宙年齡咁耐嘅時間；例如係好出名嘅旅行商問題噉，想像而家地圖上面有 n 咁多點，而個人想段演算法 output 出一條線，條線要達到
*行勻嗮咁多點、
*每點淨係經過一次、兼且
*起點同終點係同一點
三樣嘢（睇附圖）；原則上，條問題係有可能解決嘅－分析者可以索性用最夾硬嚟嗰種方法，將啲可能路線冚唪唥逐條逐條睇一次，最後畀出最短嗰條路線做 output；但如果用噉嘅方法解，部電腦就要做 n! 咁多吓運算，而 n 數值稍為大少少，n! 已經會變成天文數字。

運算複雜度相關知識，响好多實用工作上都有用，例如軟件工程噉，就成日會想做最佳化，將啲程式嘅源碼寫到「行起上嚟冇咁嘥時間同記憶體」噉嘅樣；所以軟件工程師就會參考運算複雜度知識，想要啲源碼要點寫，先可以令運算複雜度有咁低得咁低；比較學術性質嘅電腦科學工作者，仲有對運算複雜度作出數學性質嘅研究，想搵方法做到（例如）更有效率噉解運算問題。

## 基本定位

定義上，CCT 研究嘅係運算複雜度：CCT 屬運算理論嘅子領域，起源自 1960 年代中；運算理論研究運算嘅數學特性，而 CCT 係呢種研究嘅一部份，旨在想

舉個具體例子，
喺最壞情況下，用呢種夾硬嚟嘅做法解條問題，起碼要將步驟 1 至 3 做 \min(x,y)（x, y 當中數值細啲嗰個）次咁多。喺現實世界，電腦要行演算法啲步驟，就最少會到兩種資源
*時間－電腦計數快得好交關，好多時就噉睇好似係一瞬間計掂嘅噉，但實際上就算行簡單程式嗰時，部機依然要花一段極短（例如 100 ms）嘅時間嚟行；
*記憶體－部機計數就要喺計緊嗰陣記住手上嗰啲數；
CCT 想做嘅，就係攞「解決運算問題嘅演算法」做 input，畀若干個數值做 output，個 output 表示「段演算法消耗嘅資源量有幾多」（運算複雜度），當中以下呢兩種運算複雜度零舍受重視：
*時間複雜度，指段運算嘥幾多時間；
*空間複雜度，指段運算嘥幾多記憶體；
CCT 嘅分析喺應用上好重要：例如想像有條問題，可運算性理論嘅分析顯示佢係有可能用電腦解決嘅，但打後嘅分析發現，解決呢個問題要用嗰段演算法，就算用最先進嘅電腦行都要用成 100 年先行得完，噉嘅話呢條問題喺實際應用上根本冇得有效率噉解決；而且 CCT 仲能夠幫手判斷啲演算法「有幾好」－想像又有條問題，可以攞多隻唔同嘅演算法解決，而 CCT 就夠同每隻演算法畀啲數值出嚟，等電腦科學同軟件工程工作者有得客觀噉比較「邊隻演算法比較好」（正常嚟講，假設解問題嘅功效一樣，嘥嘅資源嘅量係愈少愈好）。因為噉，CCT 畀好多資訊科技工作者視為佢哋嗰行必備嘅知識。

## 符號表示

好多 IT 應用嘅工作，都涉及分析手上嘅演算法「有幾複雜」，例如家陣有班電腦科學家想提出一隻新嘅演算法，佢哋可以做嘅，就係攞隻新演算法去解問題，跟住又攞舊有嗰啲演算法解，畀人睇到「隻新演算法嘅複雜度低過打前嗰啲演算法，但解問題嗰陣嘅效用依然係咁好」。喺實際應用上，佢哋通常會將運算複雜度表達做 input 嘅大細嘅函數，即係

大 O 符號嘅做法就係建基於上面條原則嘅。喺大 O 符號之下，一隻演算法嘅時間複雜度同空間複雜度通常寫成類似噉嘅樣：
:O(n \log n)
當中 n 係個 input 嘅大細（例如如果個輸入係個數字，n 會係佢有幾多個位），O(n \log n) 反映隻演算法要行嘅步嘅數量隨 n 嘅變化，如果隻演算法要行嘅步數等如 n \log n，噉即係話
*當 n 係 10，隻演算法頂攏要行 10 步；
*當 n 係 10,000，隻演算法頂攏要行 40,000 步；
... 如此類推。數學化啲噉講，就即係如果話 f(n) = O(g(n))，就可以搵到一個正嘅常數 c，令到
:\forall_{n \ge n_0} f(n) \le c \times g(n), \qquad當中 n_0 係正整數。
假設每一步消耗嘅時間都一樣係 t_0 咁長，噉隻演算法行起上嚟要消耗嘅時間（時間複雜度）就可以輕易計埋出嚟。有咗上述嘅思考方式，就可以將啲演算法按複雜度分做：
*O(1)－隻演算法無論 n 係幾多，最大複雜度都唔變；
*O(\log n)－隻演算法嘅複雜度同 \log n 成線性關係，例如對分搜尋就係噉；
*O(n)－隻演算法嘅複雜度同 n 成線性關係，例如線性搜尋就係噉；
*O(n^2)－隻演算法嘅複雜度同 n^2 成線性關係，例如冒泡排序就係噉；
... 呀噉。上述呢啲「唔同款嘅複雜度」可以用下圖噉嘅方式想像：設 N 做「要行嘅步嘅數量最大係幾多」（c = 1），下圖裏面打戙條軸係 N 而打橫條軸就係 n；隨住 n 數值升 N 會跟住升，而「唔同款嘅複雜度」就可以想像成「唔同形狀嘅線」－

## 複雜度量度

要衡量「一段演算法運算複雜度有幾高」，有好多種做法。
### 時間同空間

時間複雜度係運算複雜度分析上最常用嘅複雜度指標，指緊「段演算法行嗮需要幾耐時間」，時間愈耐段演算法就愈算係複雜。喺實際應用上，一段演算法嘅時間複雜度唔會寫做「實際行段演算法需要幾耐時間」，而係會攞大 O 符號嚟表示。

首先，想像將段演算法寫做虛擬碼。設一條陣列 `arr`，條陣列係段演算法嘅 input 而佢長度係 `n` 咁多，以下係段演算法嘅其中一橛仔：

```fortran
statement1;
  statement2;
  statement3;
```

好似以上嘅「就噉行三行陳述式」嘅碼，無論 `n` 係幾多都一樣係行一次，所以複雜度係 O(1)，而好似以下噉嘅碼（睇埋 foreach 嘅概念）：

```fortran
同 arr 入面嘅每嚿數據，做
    statement4;
```

上面嘅碼會將 `statement4` 行 `n` 咁多次，所以複雜度係 O(n)... 如此類推。

空間複雜度係另一個運算複雜度分析上成日用到嘅指標，指「段演算法行嘅期間會霸幾多記憶體」。一般嚟講，任何要攞 input 嘅演算法行嗰陣，都實會霸一定量嘅記憶體－段演算法行嗰陣時，部電腦起碼實要搵啲記憶體記住個 input 嘅值；而除此之外，輔助空間亦都會增加一段演算法嘅空間複雜度－輔助空間泛指 input 以外嘅數據霸走嘅記憶體，即係話
:段演算法總共霸走嘅記憶體量（空間複雜度反映嘅資訊） = input 霸走嘅記憶體量 + 輔助空間
輔助空間裏面嘅數據，包括個程式會用到嘅非 input 常數同變數。

噉講即係話，要檢驗一段演算法嘅時間同空間複雜度，最基本上要䁽吓段演算法嘅碼，並且：

### 最好同最壞

最好、最壞同平均情況複雜度係分析演算法複雜度嗰陣成日會講到嘅三個指標：
*最好情況（ \Omega）：指段演算法喺「最好」（最慳時間空間）嘅情況下消耗幾多時間空間資源；
*最壞情況（ O）：指段演算法喺「最壞」（最嘥時間空間）嘅情況下消耗幾多時間空間資源；
*平均情況：指段演算法喺「平均」嘅情況下要消耗幾多時間空間資源，即係攞嗮所有可能情況，計佢哋平均要消耗幾多時間空間資源；
舉例說明，用線性搜尋演算法做例子：試諗家陣要指定一個 input 數，再由一條 input 陣列度搵個數出嚟，如果陣列入面冇嗰個數，就顯示（例如）「搵唔到」，即係
***Input**：
*#要摷嗰條陣列，同埋
*#想搵嗰個數；
***Output**：
**想要嗰個數喺陣列嘅第幾個位（好似下圖噉由 0 數起），
**如果陣列入面冇嗰個數，就出「搵唔到」噉嘅字。

要解呢條問題，線性搜尋係最簡單直接嗰種做法－即係將條陣列入面啲數逐個逐個攞嚟睇，搵到就畀嗰個數出嚟做 output，睇完嗮成條陣列都冇就畀「搵唔到」做 output。如果用線性搜尋解決條問題嘅話：
*喺最好情況下，陣列入面**有**想要嗰個數，而且個數咁啱得咁橋喺正條陣列第一格，所以電腦行 1 步就行完，用符號表達，寫 \Omega(1)；
*喺最壞情況下，陣列入面**有**想要嗰個數，但個數咁啱得咁橋喺正條陣列最尾嗰格，所以電腦行 n 步至行完（n = 條陣列嘅長度），用大 O 符號表達，寫 O(n)；
*簡化講，平均情況複雜度（A）就要噉計：
*:A = \sum_{i=1}^{n}\frac{\theta(i)}{(n+1)}, \qquad當中 \theta(i) 係指「input 大細 = i 嗰陣，要行幾多步」；
喺廿一世紀初嘅應用上，最壞情況複雜度一般畀人視為重要資訊，分析演算法嗰時實會睇；相比之下，啲人比較少會留意平均同最好情況複雜度。

## 複雜度類別

複雜度類別係運算複雜度分析上嘅一個重要概念。複雜度類別有好多款，一款複雜度類別會係一個集，包含一拃喺時間或者空間複雜度（睇返上面）上相似嘅運算問題。定義上，一款類別會講明三樣嘢：
#模型－即係話啲問題係用緊乜運算模型嚟解；喺實際應用上，用嘅運算模型通常會係圖靈機，尤其係確定型圖靈機。
#問題－「要解嘅問題係乜？要攞咩做 input 同埋畀咩 output 出嚟？」
#資源－即係講明「而家呢隻複雜度類別消耗咁多資源？係講緊乜資源？時間定記憶體？」
一款典型嘅複雜度類別（暫且嗌佢做 **X**），個定義望落會好似以下嘅句子噉：

例如 P 類別可以算係最簡單嗰隻複雜度類別，包嗮所有可以由一部確定型圖靈機（模型）喺多項式時間之內（資源）解開嘅決定問題（問題）－當中「多項式時間」意思係指段演算法「要行幾耐先解得到」嘅上限係
:O(n^k)
咁多，當中 k 係某個正嘅常數。P 類型嘅問題可以算係「能夠有效噉解」嘅問題，例如「攞個數做 input，判斷個數係唔係質數」（質數判定）就係一條 P 型問題。

除咗 P 之外，複雜度類別仲有好多種進階類型，例如 NP 噉：NP 包嗮所有可以由一部非確定型圖靈機（模型）喺多項式時間內（資源）解開嘅決定問題（問題）；解 NP 型問題嘅演算法可以喺多項式時間內「判定佢搵到嗰個答案係咪正確」－簡化講，即係話呢啲問題「難以有效率噉樣解決，不過是但攞一個 output，可以相對容易噉檢驗個 output 係咪正確答案」。

### P-NP 問題

順帶一提，P 複雜度同 NP 複雜度嘅概念仲帶出咗所謂嘅 P-NP 問題。P-NP 問題係廿一世紀初嘅電腦科學上一條好出名嘅未解問題：條問題想問以下呢樣嘢

運算問題「係咪屬 P」呢點廣受關注：由上面大 O 符號幅圖睇得出，好似（例如）
*O(2^n)
*O(n!)
等好多種唔屬 P 嘅極撈絞問題，n 數值稍為大些少，已經會見到 N 數值變到極大，用電腦行都要計極耐先計得完－呢度「極耐」係講緊「就算假設部機能夠一路係噉行永遠唔壞，計條數要用嘅時間會去到宇宙年齡（想像由宇宙大爆炸至而家呢刻之間嘅時間）嗰種級數」，而同樣 n 值嘅 P 類別問題，就淨係要用（例如）幾個鐘頭嘅時間就行完。即係話喺實際應用上，呢啲唔屬 P 嘅極撈絞問題，除非係 n 好細嘅情況，例如 n = 3 ，否則根本解唔到。噉就表示「P = NP？」呢條問題嘅答案，對於好多運算問題「實用上有冇可能解決到」有極深遠嘅影響。

到咗 2020 年代初，P-NP 問題依然係條**未解之謎**，而且仲有理論電腦科學相關嘅組織出數以百萬美金計嘅獎金，用嚟獎勵解到條問題嘅人。

## 難解問題

如果話一條運算問題屬於難解問題，意思係指嗰條問題「唔能夠有效率噉解決」。要留意嘅係，呢度講嘅「效率」唔係一般定義上嘅效率，而係

例如以下呢啲噉嘅時間複雜度都算係「能夠有效率噉解決」（唔係難解問題）
*O(n)
*O(n \log n)
*O(n^{1000000})
而以下呢啲時間複雜度就係「唔能夠有效率噉解決」（係難解問題）
*O(2^n)
*O(n!) 
簡單啲噉講，即係話一條難解問題可能理論上可以解決，甚至喺可運算性理論上證明咗係解到嘅，但電腦實際上冇可能做到喺夠短嘅時間內解條問題，即係例如如果
*時間複雜度係 O(2^n)、n = 100（呢個 n 數值已經唔算好大），而部機每秒計到 10^{12} 咁多吓運算，
部機要出答案可以消耗極大量嘅時間，數量級都仲係會去到宇宙年齡嗰種層次－喺實際應用上根本解決唔到。因為噉，要實際噉解難解問題嗰時，啲人正路唔會追求完美準確噉解決呢啲問題，而係會嘗試教部機「搵個有返咁上下理想嘅答案，然後收手」－詳情可以睇吓數學最佳化同逼近理論等嘅數學概念，亦可以睇吓著名人工智能程式 AlphaGo 。

## 子領域

*複雜度類別
*運算嘅極限
*跨運算問題
*結構運算複雜度
*描述運算複雜度
*參數運算複雜度
*量子運算複雜度
*各種數學運算嘅複雜度

## 睇埋

*軟件最佳化
*大細估計
*可運算性
*數據結構
*密碼學
*複雜博弈
*命題邏輯
*演算法熵
*奧坎剃刀

## 文獻

*Arora, S., & Barak, B. (2009). Computational complexity: a modern approach (PDF). Cambridge University Press.
*Bossaerts, P., & Murawski, C. (2017). Computational complexity and human decision-making. *Trends in Cognitive Sciences*, 21(12), 917-929.
*Bossaerts, P., Yadav, N., & Murawski, C. (2019). Uncertainty and computational complexity (PDF). *Philosophical Transactions of the Royal Society B*, 374(1766), 20180138.
*Cook, S. A. (1983). An overview of computational complexity. *Communications of the ACM*, 26(6), 400-408.
*Downey, R. G., & Fellows, M. R. (2012). *Parameterized complexity*. Springer Science & Business Media.
*Du, D. Z., & Ko, K. I. (2011). *Theory of computational complexity* (Vol. 58). John Wiley & Sons.
*Hogan, S. (2011). A gentle introduction to computational complexity theory (PDF).
*Johnson, D. S. (1985). The NP-completeness column: an ongoing guide. *Journal of algorithms*, 6(3), 434-451.
*Goldreich, O. (2008). Computational complexity: a conceptual perspective. *ACM Sigact News*, 39(3), 35-39.
*Sipser, M. (1996). Introduction to the Theory of Computation. *ACM Sigact News*, 27(1), 27-29.
*Van Leeuwen, J. (Ed.). (1991). *Handbook of theoretical computer science (vol. A) algorithms and complexity*. MIT Press.

## 註釋

## 參考
用咗嘅重要概念嘅英文名：

引用咗嘅學術文獻或者網頁：

## 拎

* 複雜度動物園
* 演算法分析 | 大 O 分析，GeeksForGeeks
* 運算複雜度理論，史丹福哲學百科全書
* 開發軟件嗰陣做表現最佳化，Medium