演算法系列之六 演算法時間複雜度計算

2022-08-20 15:30:11 字數 4084 閱讀 6690

演算法的時間複雜度的計算是乙個可以無限深入的課題。但是對於程式設計師來說,能夠大概的評估出複雜度就已經足夠用了。

在演算法這個系列之前的幾篇文章裡,介紹了演算法的使用和原理,但是我故意的將複雜度的介紹沒有寫上去。有兩個原因:第一是這個演算法複雜度介紹起來還是比較麻煩,一下子介紹不完,寫在一篇裡不太合適;第二是還沒有想好怎麼寫。

現在終於可以想得差不多了,可以開工寫寫。我的本意是盡量寫得簡單易懂,但是不把概念和內容簡化。這看起來頗有難度。如果說你讀完了本文,還是覺得沒懂。歡迎提出意見,讓我能夠進步一下,寫得更簡單一些。

首先,作為乙個演算法來說,有兩個特性:

它用來處理一定規模的資料

它處理資料 需要時間

有了這兩個基礎的認知,我們可以簡單的認為:隨著資料規模的變化,演算法處理資料需要的時間也會變化

演算法時間複雜度就是:用乙個公式來表示,這個資料規模和時間變化的關係

比如:假設乙個演算法 f,乙個資料集 n;

可以看到,隨著資料規模的增長,演算法處理需要的時間也在變化。如果我們想要表達這個時間變化的趨勢和資料集n的關係的話,我們假設這個關係是 \(t(n)\) 我們可以寫下面的乙個算式:

\[t(n) = f(n)\]

這裡的 \(t(n)\) 表示了乙個演算法對於資料集n需要使用的時間的精確計算。

在實際上,精確計算公式是不可能被統計計算出來的,因為不同的語句,不同的操作,耗費的時間也是不一樣。所以,在實際應用中,我們不需要精確計算,只需要乙個公式,來表達 時間花費和資料集大小的漸近關係。

所以,在描述演算法的時候,一般採用 大o(big-o notation)表示,又稱為漸進符號,

big-o 是用於描述函式漸近行為的數學符號。更確切地說,它是用另乙個(通常更簡單的)函式來描述乙個函式數量級的漸近上界。 -- 《維基百科》

上面的解釋太玄幻。

其實簡單來說,乙個函式是幾個項的和,每個項都能被歸入到乙個數量級,比如(平方、立方)。當資料集(自變數)趨於無窮大時,數量級大的變化太大,造成了數量級小的部分對函式結果的影響小到可以被忽略。

所以,我們就簡單的使用數量級大的部分來替代整個函式,來簡化表達。

比如:\[t(n) = 2n^3 + n^2 + 1024\]

這個 \(t(n)\) 表達了某個演算法的時間複雜度。

我們可以將這個等式分為三個項,按照數量級大小降序:

我們對n取幾個值來分別看一下 a 、b 、c 三個部分的值:

當n=10;a=2000,b=100,c=1024

當n=100;a=2000000,b=10000,c=1024

當n=1000;a=2000000000(20億),b=1000000(100萬),c=1024

當n=10000;a=2000000000000(2兆),b=100000000(1億),c=1024

就不計算了吧……

可以看到,當資料集變大的時候,數量級大的部分增長的速度遠遠超過數量級小的部分。當n增大時,a佔主導,b 和 c 兩個部分的值相對於a部分來說,對結果的影響就可以忽略不計了。

然後,再看 a 部分,決定 a 部分大小的是 \(n^3\)。當我們要表達乙個漸進關係的時候,常數並不影響這個關係的表達,所以 a 部分中的 常數就可以忽略。

最終,變成了下面:

\[t(n) = o(n^3)\]

這個 \(o(n^3)\) ,就是演算法的時間複雜度。

tips: 上面的例子中,a部分常數是 2。可以放心的忽略。但如果是常數是 \(10^3\),忽略的話,結果就是錯誤的了。所以要對可能的大常數敏感。

明白了如何表示演算法的複雜度。那如何計算複雜度呢?

給出乙個段簡單的**:將陣列中每個元素的值加1。

我們分析它的語句執行的次數:

所以這裡的時間複雜度是:

\[t(n) = n + 1 \to o(n)\]

再來乙個例子: 計算氣泡排序的演算法複雜度

void sort(int *a, int len) 

swap(a, min, i);}}

對於基於比較的排序演算法來說,我們在計算複雜度的時候,是計算比較了多少次。

總的比較次數就是 \(n + (n-1) + (n-2) + \ldots + 1 \),就是n的階加。根據階加公式可得:

\[ n\sim\quad = \frac 2 \rightarrow o(n^2)\]

那麼,氣泡排序的時間複雜度就是 \(o(n^2)\)。

在上面的兩個例子中, 演算法是穩定的。什麼叫穩定呢? 就是不管資料集的內容是什麼樣的,以什麼方式來組合,演算法都是以這個複雜度來執行。

拿氣泡排序來說,不管給到的原始陣列是排好序的、倒序的、亂序的,比較次數都是一樣的,不存在演算法的最好情況和最壞情況,這樣的演算法叫做穩定演算法。

但是比如像 快速排序、堆排序 都是不穩定的演算法。因為這類演算法依賴於原始資料集的內容和其內容的組合方式。例如快速排序,如果原始陣列是乙個接近有序的,那麼它的速度會快很多,如果原始陣列是倒序的,那麼快排的效能會急劇的惡化。而且,快排的效率還依賴於基準數的選擇。

但是,對於資料集的分析這件事情,實在是太難了,遠遠超過了普通程式設計師的能力(包括我

演算法時間複雜度計算

本部落格主要講解下演算法時間複雜度的基本計算過程。演算法時間複雜度是指演算法中基本操作的執行次數。記為t n o f n t n 為增長比最快項的係數。計算步驟 舉例說明 例1 void funfirst int n step1 基本操作是 j i 2 step2 確定規模,根據迴圈條件,確定規模為...

演算法系列 衡量的標準之時間複雜度

前言 數學中有演算法,計算機中亦有演算法,此處演算法系列討論的是計算機中的演算法。說到演算法,就要聊一聊演算法的複雜度了。什麼是複雜度?為什麼要知道複雜度?如何衡量複雜度?了解這些,是我們寫好演算法的基礎。下面我們就來聊一聊演算法世界的標準,演算法的複雜度。什麼是演算法複雜度,聽到這個名字,就是問號...

演算法時間複雜度的計算

定義 如果乙個問題的規模是n,解這一問題的某一演算法所需要的時間為t n 它是n的某一函式 t n 稱為這一演算法的 時間複雜性 求解演算法的時間複雜度的具體步驟是 1 找出演算法中的基本語句 演算法中執行次數最多的那條語句就是基本語句,通常是最內層迴圈的迴圈體。2 計算基本語句的執行次數的數量級 ...