當我們在評估一個一段 code 的品質,時間與使用空間是一個很好的評估標準。好的 code 可以在較短的時間內達成任務,而比較差的則會花比較多時間。
然而,在不同時間、不同電腦環境下,程式執行起來的時間一定會有所差別。假設今天在兩組不同的 code 分別在不同的電腦執行相同任務,那們我們測出來的時間勢必會受到環境的影響。就算是在同一台電腦,也會因為時間點不同而產生差異。因此無法準確以時間去估計 code 的品質。
因此我們只能回歸到程式碼本身的執行步驟來評估其品質
考慮以下程式碼片段
可以發現程式碼有 個步驟,而且不管在什麼情況下,一定只會有三個步驟
如果加上一個迴圈使輸出執行 次:
在這個 for 迴圈中:
一開始賦值 ,接下來反復判斷 是否小於 ,並加上 ,然後輸出
因此程式碼總共 個步驟
迴圈次數會隨著輸入數字大小不同而變化:
在這個 for 迴圈中:
一開始賦值 ,接下來反復判斷 是否小於 ,並加上 ,然後輸出
因此程式碼總共 個步驟
巢狀迴圈:
因此程式碼總共 個步驟
由上述幾個例子可以發現,步驟數量有可能因迴圈數而變成一個多項式函數
假設今天有兩個 code,其步驟數量分別為: 、
可以發現一個問題: 在 、兩者的步驟數量剛好皆為
在此之前綠線的步驟數皆小於紅線,在此之後綠線的步驟數皆大於紅線
那麼該如何描述兩著的大小關係呢?
若且為若存在正的常數 與 使
舉例像是 ,絕對可以找到一個函數 使
如此一來,我們就可以說 為 ,就是 的時間複雜度為
基本上,如果 的最大冪次是 ,則可稱 為
口頭上可以說時間複雜度為
回到剛才的問題,由於 、 的的時間複雜度分別為 、
因此紅的程式碼時間複雜度比綠的程式碼小,原因是因為在圖中某個點以後,紅必定小於綠
根據微積分的觀念,在 非常大的時候 只會受到最大的一項影響
因此在多數時候,可以直接找在 中最大的那一項去判斷
例: ,則,因為當 非常大時, 最大
例: ,則,因為當 非常大時, 最大
: 常數時間,通常沒有迴圈,或是迴圈數固定,要注意常數有時可能非常大
: 線性時間,通常就是遍歷資料一次
: 平方時間,通常就是開兩個迴圈,或是將一維資料拓展成二維
: 立方時間,不常見,但通常是在走訪三維空間
: 對數時間,二分搜尋或是在某些資料結構 (heap、DSU 等) 都有可能出現
: 在很多排序法或是資料結構上會出現
: 階乘時間,別用他
: 別用他
: 根號時間,在某些數論問題或是區間分塊 (莫隊演算法) 會出現
由於在 Big-O 所估計的是上限的概念,所以當然也有估算下限的方式(方法差不多)
由於上述的三個方法都有一個問題,假設常數 很大,大到其資料量遠所不及,根本不可能達到,那麼自然會失準。因此有 Amortized analysis 來更準確地去估計他們的量,其中就包含三種方法:
時間複雜度可以想像成是資料量與步驟數量之間的關係
其實如果說一段程式很輕易的就可以看出來哪個地方迴圈數最多,那幾乎就可以由此判斷時間複雜度。假如說有一個三層的巢狀迴圈,那麼就可以猜他是不是 雖然有時候不一定是
有時候 Big-O 裡面不一定是 ,在圖論問題中有可能是 (節點) 或 (邊);在數論問題中, 可能只是一個整數
總之,在估算之前,要先看清楚要估算哪些資料
ShanC 程式競賽筆記
作者: ShanC
更新: 2024/9/27