1. 程式人生 > >復雜度分析(一)

復雜度分析(一)

內存占用 程序 統計 是我 int 把他 span 行操作 nlog

(一) 復雜度分析的由來

我們平時寫代碼的時候,想要知道一段代碼的運行時間,占用空間等等,一般都是在代碼開始的記錄一下當前時間,運行結束的時候,再記錄一下時間,最後得出這段代碼的運行時間,一般就是通過這個來判斷我們的代碼的執行效率。這種做法沒有錯,但是這樣做統計出來的。

在我之前寫業務代碼的時候,比如增刪查改的時候,我經常都是這麽幹的,為了讓一個查詢更快,更有效率,邊調試,邊分析,找出慢的步驟,再逐一解決。那麽問題來了,我們還需要學這個算法的時間復雜度分析嗎?

對於這個問題,之前我也是很茫然,看到算法時間復雜度分析的相關內容,我就直接忽略,想著工作中也不會這麽分析。

那麽,我先解釋一下,上面這種做法有什麽弊端呢(其實上面的做法,真心還行)。 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)相乘。

技術分享圖片

復雜度分析(一)