復雜度分析(一)
(一) 復雜度分析的由來
我們平時寫代碼的時候,想要知道一段代碼的運行時間,占用空間等等,一般都是在代碼開始的記錄一下當前時間,運行結束的時候,再記錄一下時間,最後得出這段代碼的運行時間,一般就是通過這個來判斷我們的代碼的執行效率。這種做法沒有錯,但是這樣做統計出來的。
在我之前寫業務代碼的時候,比如增刪查改的時候,我經常都是這麽幹的,為了讓一個查詢更快,更有效率,邊調試,邊分析,找出慢的步驟,再逐一解決。那麽問題來了,我們還需要學這個算法的時間復雜度分析嗎?
對於這個問題,之前我也是很茫然,看到算法時間復雜度分析的相關內容,我就直接忽略,想著工作中也不會這麽分析。
那麽,我先解釋一下,上面這種做法有什麽弊端呢(其實上面的做法,真心還行)。 1、我們通過這種代碼得出來的運行時間是很依靠機器的硬件的,要看你CPU的計算能力,內存的大小等等(不是很多時候,都會有這麽一個情況,線下測試很快,線上慢成狗了,因為沒有考慮到CPU的分配情況,內存占用情況)。2、這種統計方法受到數據規模的影響很大,在測試環境,測得再OK,上線之後還是問題很大)。
基於上面這些問題,IT的大牛們,就想了一下,要不對算法的時間復雜度來個抽象,不要依賴於機器,硬件等等做法呢。所以他們就提出了一種做法就做O(n),時間復雜度做法。
(二)什麽是時間復雜度O(n)
時間復雜度:它並不是表示代碼的真正運行時間,而是表示代碼的執行時間隨數據規模增長的變化情況。
下面來看這個例子。
int GetSum(int n) { 1 var sum = 0; 2 for (var i = 0; i < n; i++) { 3 sum += i; } return sum; }
我們知道每個語句的執行操作,從CPU的角度來看,就是讀數據--運算--寫數據,假設這整個操作需要一個單位的時間。我們假設一行代碼就是一個單位的執行時間,那麽這段代碼就是2+2n個單位時間,從這裏可以看出來程序的執行時間是和n成正比的。我們把這個規律總結成為一個公式,就是我們的時間復雜度。
(三)時間復雜度的分析方法
1、關註執行次數最多的那段代碼,那個就是這整段代碼的時間復雜度
2、乘法法則,兩個時間復雜度相乘,就是整個時間復雜度。(嵌套內外代碼的復雜度等於內外復雜度的乘積)
3、總的復雜度等於最大的那個復雜度。(一般兩個復雜度,不是同一個層級的時候,才取最大的,比如o(n)和o(n2),則取o(n2),如果一個是o(n),另外一個是o(m),則時間復雜度就是o(m+n))
(四)總結
感覺這個時間復雜度分析,就像設計模式,你說按照設計模式寫出來的代碼,也不一定是最好的。我們時間復雜度也是一樣,也不一定說o(n)就比o(n2)的好,但是只要當我們一提到某某某設計模式的時候,心裏馬上就可以勾勒出這個設計模式的代碼組成結構。當我們一說o(n)的時候,也可以知道這段代碼是如何組織的。
還有很多常見的時間復雜度,比如o(1),o(n) o(logn) o(nlogn),o(n2)。下面說說我覺得最難分析的o(logn)這個時間復雜度把。
int GetSum(int n) { var i = 1; while (i<=n) { i = i*2; } return i; }
上面這個例子,一步步來分析,i=2,I=4,i=8,i=16,一直到i<=n。 那這個代碼執行了多次呢。就是2的x次方小於等於n。 現在要求這個x是多少呢,那就用到我們的對數了x=log2為底,n為真數的對數了。
int GetSum(int n) { var i = 1; while (i<=n) { i = i*3; } return i; }
這個例子也是和上面是一樣的,它是Log3為底,n為真數的對數。
我們知道對數是可以相互轉化的了 。對數都是可以相互轉化的,我們同時把他們轉化為以十為底的對數,然後去掉常量,就得到了o(logn)。然後o(nlogn)就是n個o(logn)相乘。
復雜度分析(一)