1. 程式人生 > >大O表示法初學者指南

大O表示法初學者指南

大O表示法初學者指南

原文地址:https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/ 
電腦科學中,大O表示法被用來描述一個演算法的效能或複雜度。大O表示法可以用來描述一個演算法的最差情況,或者一個演算法執行的耗時或佔用空間(例如記憶體或磁碟佔用)。 
相信許多人讀過《Programming Pearls》(《程式設計珠璣》)或者其他電腦科學書籍時,在看到大O符號或者其他奇怪的語法符號時都會感覺到自己遇到了一堵無法翻越的高牆。那這篇文章將會帶領大家對大O表示法以及對數級演算法有一個最簡單的認識。 
首先作為一個程式設計師,其次作為一個數學家,我(Rob Bell)發現透徹的理解大O表示法的最好方式就是來嘗試一些程式碼示例。下面按照演算法的增長級別,展示了一些常見的演算法描述,同時針對每個演算法給出了一個簡單的示例。

O(1)

O(1)表示該演算法的執行時間(或執行時佔用空間)總是為一個常量,不論輸入的資料集是大是小。

O(N)

O(N)表示一個演算法的效能會隨著輸入資料的大小變化而線性變化。下面的例子同時也表明了大O表示法其實是用來描述一個演算法的最差情況的:在for迴圈中,一旦程式找到了輸入資料中與第二個傳入的string匹配時,程式就會提前退出,然而大O表示法卻總是假定程式會執行到最差情況(在這個例子中,意味著大O會表示程式全部迴圈完成時的效能)。

bool ContainsValue(IList<string> elements, string value)
{
    foreach (var element in elements)
    {
        if (element == value) return true;
    }

    return false;
}

O(N^2)

O(N^2)表示一個演算法的效能將會隨著輸入資料的增長而呈現出二次增長。最常見的演算法就是對輸入資料進行巢狀迴圈。如果巢狀層級不斷深入的話,演算法的效能將會變為O(N3),O(N4),以此類推。

bool ContainsDuplicates(IList<string> elements)
{
    for (var outer = 0; outer < elements.Count; outer++)
    {
        for (var inner = 0; inner < elements.Count; inner++)
        {
            // Don't compare with self
            if (outer == inner) continue;

            if (elements[outer] == elements[inner]) return true;
        }
    }

    return false;
}

O(2^N)

O(2^N)表示一個演算法的效能將會隨著輸入資料的每次增加而增大兩倍。O(2^N)的增長曲線是一條爆炸式增長曲線——開始時較為平滑,但資料增長後曲線增長非常陡峭。一個典型的O(2^N)方法就是裴波那契數列的遞迴計算實現。

int Fibonacci(int number)
{
    if (number <= 1) return number;

    return Fibonacci(number - 2) + Fibonacci(number - 1);
}

對數

要說明對數情況,稍稍有點複雜,因此我將使用一個非常通用的示例: 
二分查詢是一種用來在有序集合中進行查詢的高效演算法。二分查詢從資料集的中間位置開始,然後用這個中間值和一個目標值進行比較。如果比較結果為相等,則程式返回成功。如果目標值大於中間值,程式會擷取從中間值開始到最大值的那段資料集,並重復執行同樣的查詢方法。想死的,如果目標值小於中間值,程式將會繼續在資料集中較小的那一半執行二分查詢。二分查詢程式會持續的將資料集對等分,以進入下一次迴圈,直到最終找到與目標值相等的資料後,程式就退出。 
這類演算法的效能就會被描述為O(logN)。正是通過這種不斷對資料進行對等分的二分查詢操作,使得二分查詢演算法的曲線從一個峰值開始,隨著輸入資料集的增長而慢慢的變得平緩。用例子來說明的話,例如一個包含10個輸入資料的程式需要耗時一秒完成,則一個包含100個輸入資料的程式就需要耗時兩秒,然後一個包含1000個輸入資料的程式就耗時三秒。加倍的輸入資料對這類演算法的效能結果影響非常小。基於如此,類似於二分查詢的對數級演算法在處理大量資料集時非常高效。 
本文僅僅覆蓋了關於大O表示法以及對數演算法非常基本的知識概念。如果想要更深入的理解他們,可以參考維基百科: 
大O表示法:https://en.wikipedia.org/wiki/Big_O_notation 
對數:https://en.wikipedia.org/wiki/Logarithm