---
title : 時間與空間複雜度
tags : article
---
# 時間與空間複雜度
### 程式解題時的一些常用術語
| short | full-name | |
| ----- | ------------------- | ---------- |
| AC | Accept | 答案正確 |
| WA | Wrong Answer | 答案錯誤 |
| TLE | Time Limit Exceed | 超時 |
| MLE | Memory Limit Exceed | 記憶體超量 |
| RE | Runtime Error | 執行錯誤 |
| CE | Compile Error | 編譯錯誤 |
---
### 程式的執行時間分析
電腦每秒可以執行多少次,取決於CPU,
以i7-10代的電腦來說,其CPU平均執行速度為2.53GHz ~ 5GHz (GHz = $10^9$ proces. per second)
而我們通常評估程式的時候,都會以<font color=red> $10^8$~$10^9$</font> 來衡量,此數值意義是代表<font color=red>每秒可以執行幾次</font>,
也就是你評估出當前題目的時間複雜度會超過這個數值時,就可能會TLE。
---
### 程式的記憶體分析
對於一個程式的記憶體用量,取決於<font color=red>當前程式所佔用的記憶體最大值</font>,
而對於大部分的程式片段來說,其記憶體上限為 <font color=red>4GB (4 Giga-Bytes)</font>。
> 在電腦算數中 $\text{1K} = 2^{10} \approx 10^3, \text{1M} = 2^{20} \approx 10^6, \text{1G} = 2^{30} \approx 10^9$

> <font color=red>請務必牢記以上的紅色數字</font>
> 以上這些知識在大三的演算法分析時會有更詳細的教學
---
### Big-O 表示法
其記錄數值的方式,便是用一個字母「O」的函式形式做表達,
其表示法常會省略掉 <font color=blue>係數、常數項、低次項</font> 的部分,以下舉例:
`ex.` $50n$ <font color=red>$⇒ O(n)$ </font>
`ex.` $7n^2+3n+1$ <font color=red>$⇒ O(n^2)$ </font>
`ex.` $10⋅(\log_2n)^3 + 1000⋅\log_2n$ <font color=red>$⇒ O(\log^3n)$ </font>
`ex.` $\sqrt{n}+(\log_2n)^3+\log_2n$ <font color=red>$⇒ O(\sqrt n)$ </font>
(請注意,log函式的省略底數格式,在電腦時間分析相關部分,其底數通常為2)

| | $n=10$ | $n=100$ | $n=10^4$ | $n=10^8$ | $n=10^{16}$ |
| ----------- | --------- | --------- | --------- | --------- | ----------- |
| $n!$ | 3628800 | <font color=red>9.33e+157</font> | | | |
| $2^n$ | 1024 | <font color=red>1.267e+30</font> | <font color=red>1.9e+3010</font> | <font color=red>3e+3e+7</font> | <font color=red>1e+3e+15</font> |
| $n^2$ | 100 | 10000 | 1e+8 | <font color=red>1e+16</font> | <font color=red>1e+32</font> |
| $n\log_2 n$ | 33.219280 | 664.38561 | 132877.12 | <font color=red>2.6575e+9</font> | <font color=red>5.315e+17</font> |
| $n$ | 10 | 100 | 10000 | 1e+8 | <font color=red>1e+16</font> |
| $\sqrt n$ | 3.1622776 | 10 | 100 | 1000 | 1e+8 |
| $\log_2 n$ | 3.3219280 | 6.6438561 | 13.287712 | 26.575424 | 53.150849 |
(紅色部分是超過 $10^8$ 的數值)
---
### 時間複雜度(Time Complexity)
一個程式,或是一個演算法的時間效率,通常會記做「時間複雜度(Time Complexity)」,
常以 **Big-O** 來表示,以下舉例:
```cpp
int total = 0;
for(int x = 0 ; x<N ; x++)
total += total + x;
// 單迴圈,計算二階差級數和
// Time Complexity : O(N)
```
```cpp
int m[N][N];
for(int x = 0 ; x<N ; x++){
for(int y = 0 ; y<N ; y++)
cout << m[x][y] << ' ';
cout << '\n';
}
// 雙迴圈,印出 N×N 的陣列
// Time Complexity : O(N^2)
```
```cpp
for(int x = 0 ; x<N ; x++){
for(int y = 0 ; y<N ; y++);
for(int z = 0 ; z<M ; z++);
}
// 複合雙迴圈,
// Time Complexity : O(N(N+M))
```
```cpp
void run(int n){
if(n)
run(n-1);
}
// 遞迴
// Time Complexity : O(N)
```
```cpp
void run(int n){
if(n)
run(n>>1); // n>>1 == n/2
}
// 二分遞迴
// Space Complexity : O(logN)
```
---
### 空間複雜度(Space Complexity)
一個程式,或是一個演算法的(記憶體)空間用量,通常會記做「空間複雜度(Space Complexity)」,
也可使用 **Big-O** 來表示,以下舉例:
```cpp
int arr[N][M];
// 二維陣列
// Space Complexity : O(NM) = NM bytes
```
```cpp
int SumOfRange(int L, int R){
if(L>R)
return 0;
return L + SumOfRange(L+1,R);
}
// 遞迴形式的區間和 (N = R-L)
// Time Complexity : O(N)
// Space Complexity : O(N)
// Max-Memory Usage : 8N bytes
```
```cpp
int binary_search(int*arr, int L, int R, int tar){
if(L>=R)
return arr[L] == tar ? L : -1;
int mid = (L+R)/2;
if(tar > arr[mid])
return binary_search(arr,mid+1,R,tar);
else
return binary_search(arr,L,mid-1,tar);
}
// 遞迴版的二分搜尋法,N為陣列arr的長度 = R - L
// Time Complexity : O(logN)
// Space Complexity : O(logN)
// Max-Memory Usage : 20logN bytes
```
```cpp
int arr[100];
int L = 0, R = 100, tar, mid;
while(L<R){
mid = (L+R)/2;
if(tar > arr[mid])
L = mid+1;
else
R = mid-1;
}
// 迴圈版的二分搜尋法,N為陣列arr的長度
// Time Complexity : O(logN)
// Space Complexity : O(1)
// Max-Memory Usage : 416 bytes
```
請注意,一段程式區段(scope`{}`)執行完後,裡面所宣告的所以變數都會被釋放(包含指標變數),
但被 new 的物件並不會被釋放,這些被 new 的物件必須要被 delete 後才會被釋放。
而記錄一個程式的空間複雜度,我們會用整個程式最大會一次占用多少記憶體來記錄。
> 通常紀錄程式的空間複雜度,不太會用Big-O表示,因為其很容易計算也很容易過量使用。
> <font color=red><友善提醒></font>
請務必注重 <font color=blue>O(logN)算法</font> 的潛力,其執行時間<font color=black>遠快</font>於其他任何的執行時間。
也因此,有一句話是這樣說的:<font color=black>「高階的程式設計應當避免使用迴圈」</font>