演算法時間複雜度的表示法O(n2)、O(n)、O(1)、O(nlogn)等是什麼意思?

我知道他們表示空間複雜度,我想知道的是他們本身是什麼意思.....


大體上和 @丁戍 說的差不多。

簡單說O(n2)表示當n很大的時候,複雜度約等於Cn2,C是某個常數,簡單說就是當n足夠大的時候,n的線性增長,複雜度將沿平方增長。

O(n)也是差不多的意思,也就是說n很大的時候複雜度約等於Cn,C是某個常數。

O(1)就是說n很大的時候,複雜度基本就不增長了,基本就是個常量C。

參見維基:

大O符號


恰好最近正在擼《演算法》第四版,是時候祭出這張圖了。

時間複雜度這個東西,其實更準確點說應該是描述一個演算法在問題規模不斷增大時對應的時間增長曲線。所以,這些增長數量級並不是一個準確的性能評價,可以理解為一個近似值,時間的增長近似於logN、NlogN的曲線。


簡單理解:就是變數為n的時候,演算法需要對變數操作次數的量級。

比如:

要找到一個數組裡面最大的一個數,你要把n個變數都掃描一遍,操作次數為n,那麼演算法複雜度是O(n).

用冒泡排序排一個數組,對於n個變數的數組,需要交換變數位置n^2 次,那麼演算法複雜度就是O(n^2 ).

有時候,如果對變數操作的次數是個多項式比如n^4+n^2+n, 就取數量級最大的那個,O(n^4)


其實如題主所說,他們本身是有意思,而且可以根據他們本身的意思去理解時間複雜度的概念。

在數學上定義: 存在常數 c,使得當 N &>= c 時 T(N) &<= f(N),表示為 T(n) = O(f(n)) 。

我們先從頭開始看:

我們假設計算機運行一行基礎代碼需要執行一次運算。

int aFunc(void) {
printf("Hello, World!
"); // 需要執行 1 次
return 0; // 需要執行 1 次
}

那麼上面這個方法需要執行 2 次運算

int aFunc(int n) {
for(int i = 0; i &< n; i++) { // 需要執行 (n + 1) 次 printf("Hello, World! "); // 需要執行 n 次 } return 0; // 需要執行 1 次 }

這個方法需要 (n + 1 + n + 1) = 2n + 2 次運算。

我們把 演算法需要執行的運算次數 用 輸入大小n 的函數 表示,即 T(n) 。

此時為了 估算演算法需要的運行時間 和 簡化演算法分析,我們引入時間複雜度的概念。

數學上定義: 存在常數 c,使得當 N &>= c 時 T(N) &<= f(N),表示為 T(n) = O(f(n)) 。

怎麼理解呢?

如圖:

當 N &>= 2 的時候,f(n) = n^2 總是大於 T(n) = n + 2 的,於是我們說 f(n) 的增長速度是大於或者等於 T(n) 的,也說 f(n) 是 T(n) 的上界,可以表示為 T(n) = O(f(n))。

因為f(n) 的增長速度是大於或者等於 T(n) 的,即T(n) = O(f(n)),所以我們可以用 f(n) 的增長速度來度量 T(n) 的增長速度,所以我們說這個演算法的時間複雜度是 O(f(n))。

所以就有了我們現在理解的時間複雜度,記作: T(n) = O(f(n)),用來度量演算法的運行時間。它表示隨著 輸入大小n 的增大,演算法執行需要的時間的增長速度可以用 f(n) 來描述。

顯然如果 T(n) = n^2,那麼 T(n) = O(n^2),T(n) = O(n^3),T(n) = O(n^4) 都是成立的,但是因為第一個 f(n) 的增長速度與 T(n) 是最接近的,所以第一個是最好的選擇,所以我們說這個演算法的複雜度是 O(n^2) 。

可以參考 十分鐘從零搞定時間複雜度


Stackoverflow上有一個類似的問題,在演算法標籤下的贊同數排第一,你可以參考一下:algorithm - What is a plain English explanation of "Big O" notation?


加減乘除均為一個「單位」的複雜度,那麼例如

for(int i=0;i&

{

for(int j=0;j&a++; //注意,這裡計算一次的時間是1.

}

那麼上面的這個例子的時間複雜度就是 m*n(a++執行了m*n次)

再例如冒泡排序的時間複雜度是N*N;快排的時間複雜度是log(n)。

詳細的情況,建議你看《演算法導論》,裡面有一章節,具體講這個的。


也來說一下。雖說大O是表示相比幾乎是「正比」,但不完全是正比。
是指當數據量 n 很大時,你這個程序的計算量所耗的時間差不多是處理 n^2 那個級數。
也就是說, lim n→∞ N / n^2 = k &< ∞。 (這裡 k &> 0。如果 k= 0 那就是小 o,
 表示你的程序比起 n 來說不算什麼,這幾乎不可能。)
 
大 O 小 o 記號在數學上常用,
都是拿來比較「無窮大」之間的量級或者「無窮小(近乎0)」間誰較小。

具體一點來說,假如你的矩陣相當大(你有一堆輸入原料與成本的搭配),
那你要計算矩陣相乘,如果沒有好的演算法,你就幾乎要計算n^3次。
課本練習題的小階手算你還不痛不癢,
可是當你是替工廠規畫一整堆的管線時,
不要說手算,就是計算機去跑都會跑到天荒地老!
因此開發出好的演算法(或至少針對特殊矩陣開發出好演算法)就相當重要。
 
而此時數學上「無窮大的量級比較」又是個重要知識了。
 
我們知道,
log n、P(n) (n 的多項式)、e^n、n! 都隨著 n 增大而趨向無窮大,
可是人比人氣死人,這幾個「無窮大」相比起來可又是天淵之別。
在 n 愈跑愈大的時候,
log n 比起 n^2 來說幾乎不算什麼, n^2 比起 e^n 又簡直不算什麼。
而 n! 則甚至比幾何級數還可怕!
(這就是爲何手算習題疊代很可愛,可是交給計算機最好別每次代回。)
大約如此。


有興趣可以看一看演算法導論的前幾章。


大O和小o、大Ω 小ω、Θ統稱asymptotically notation,在離散數學有詳細的介紹,核心是極限的概念


《數學分析》無窮大量的比較 等階無窮大


這麼理解吧,那三個符號就是發明出來描述演算法複雜度的,發明者想用啥符號就用啥符號。

具體描述前,你要再次理解演算法複雜度這個概念。演算法複雜度的內涵是:隨著輸入規模n的變化,語句執行總次數(T(n))的變化規律。我們通常用函數來描述某種變化規律。所以發明者就要找一些函數啊,n,lg(n)之類的,基本就幾個函數,就能把上面說的"變化規律"的所有情況涵蓋完了,並不需要那些炒雞複雜的函數了 。

那麼現在的問題是,怎麼利用這幾個函數來描述變化規律呢。

假設我們要描述某個變化規律f,我們是不是要在那幾個函數中找個函數圖像貼近f的函數圖像呢。那就找吧,找的時候發現一些不同的"貼近"的情況,貌似兩個圖像有幾種不同的貼近方法呢,是從上往下貼近,還是從下往往上貼近,還是基本重合呢。這三種情況囊個描述呢,發明者乾脆發明三個符號描述這三種情況吧。於是就有了。。。。神奇三符


謝邀。我覺得你指的應該是時間複雜度…

大O函數指的是演算法所耗時與輸入規模之間的關係。不同的排序演算法複雜度不一定相同。比如O(n^2)代表這個演算法排序n個數,所耗CPU時間與n的平方在同一個數量級。

空間複雜度則是指空間與問題規模的關係。

演算法時空複雜度是衡量一個演算法優劣的主要因素。


推薦閱讀:

求基於比較的最快中位數演算法(最壞情況是O(n))?

TAG:演算法 | 演算法複雜度 | 排序演算法 |