Big-O 表示法

定義

Big-O 表示法的核心是數學符號,用於比較函式的收斂速度。讓 n -> f(n)n -> g(n) 成為自然數上定義的函式。然後我們說 f = O(g) 當且僅當當 n 接近無窮大時 f(n)/g(n) 有界。換句話說,f = O(g) 當且僅當存在常數 A 時,對於所有 n,f(n)/g(n) <= A

實際上,Big-O 表示法的範圍在數學上有點寬泛,但為了簡單起見,我將其縮小到演算法複雜度分析中使用的範圍:在自然界定義的函式,具有非零值的函式,以及 n 增長的情況到無窮遠。

這是什麼意思?

我們來看一下 f(n) = 100n^2 + 10n + 1g(n) = n^2 的情況。很明顯,這兩個函式都傾向於無窮大,因為 n 傾向於無窮大。但有時知道限制是不夠的,我們也想知道功能接近極限的速度。像 Big-O 這樣的概念有助於通過收斂速度來比較和分類函式。

讓我們通過應用定義來找出 f = O(g)。我們有 f(n)/g(n) = 100 + 10/n + 1/n^2。由於當 n 為 1 並且正在減小時 10/n 為 10,並且由於當 n 為 1 並且還在減小時 1/n^2 為 1,所以我們有 f(n)/g(n) <= 100 + 10 + 1 = 111。定義是滿意的,因為我們已經找到了 f(n)/g(n)(111)和 f = O(g) 的界限(我們說 f 是 n^2 的 Big-O)。

這意味著 f 在與 g 大致相同的速度下傾向於無窮大。現在這似乎是一件奇怪的事情,因為我們發現 f 最多比 g 大 111 倍,換句話說,當 g 增長 1 時,f 增長最多為 111.這似乎在增長快 111 倍並不是大致相同的速度。事實上,Big-O 符號並不是一種非常精確的方法來對函式收斂速度進行分類,這就是為什麼在數學中我們在需要精確的速度估計時使用等價關係 。但是為了在大速度類中分離演算法,Big-O 就足夠了。我們不需要將相互之間生成固定次數的函式分開,而只需要無限增長的函式 ** 比彼此快。舉例來說,如果我們採取 h(n) = n^2*log(n),我們看到 h(n)/g(n) = log(n) 趨向於無窮大與正因此 h 是為 O(n ^ 2),因為^ h 成長無限小於 n ^ 2 更快。

現在我需要做一個旁註:你可能已經注意到,如果 f = O(g)g = O(h),那麼 f = O(h)。例如在我們的例子中,我們有 f = O(n^3)f = O(n^4) …在演算法複雜度分析中,我們經常說 f = O(g) 意味著 f = O(g) g = O(f),可以理解為“g 是 f 的最小 Big-O”。在數學中我們說這些函式是彼此的大相互作用。

怎麼用?

在比較演算法效能時,我們對演算法執行的運算元感興趣。這稱為時間複雜度。在這個模型中,我們認為每個基本操作(加法,乘法,比較,賦值等)需要一定的時間,我們計算這些操作的數量。我們通常可以將此數字表示為輸入大小的函式,我們稱之為 n。遺憾的是,這個數字通常會隨著 n 增長到無窮大(如果沒有,我們說演算法是 O(1))。我們在 Big-O 定義的大速度類中分離我們的演算法:當我們談論“O(n ^ 2)演算法”時,我們的意思是它執行的運算元,表示為 n 的函式,是 O( N ^ 2)。這表示我們的演算法大致與演算法一樣快,該演算法可以執行大量等於其輸入大小的平方的演算法,或更快或更快部分是因為我使用 Big-O 而不是 Big-Theta,但通常人們會說 Big-O 意味著 Big-Theta。

在計算操作時,我們通常會考慮最壞的情況:例如,如果我們有一個最多可以執行 n 次且包含 5 次操作的迴圈,那麼我們計算的運算元就是 5n。也可以考慮平均案例複雜性。

快速說明:快速演算法是執行少量操作的演算法,因此如果運算元量增長到無限,則演算法速度較慢O(n) 優於 O(n ^ 2)。

我們有時也對演算法的空間複雜性感興趣。為此,我們將演算法佔用的記憶體中的位元組數視為輸入大小的函式,並以相同的方式使用 Big-O。