時間複雜度的定義,記號以及幾種計算方法

2021-09-03 07:36:07 字數 3952 閱讀 9445

如果接觸過演算法,那麼對於演算法的時間複雜度分析一定不陌生,因為時間複雜度是演算法優劣的乙個重要評價標準。

對於乙個演算法,我們不光關心它在某個規模輸入的情況下耗時多少,我們更關心的是,當輸入規模瘋狂增長的時候,演算法耗時增加多少,是按線性形式增加的?指數增加的?對數形式增加的?比如當你的演算法接收的輸入規模從k個數變到nk個數時,你的耗時是增加了n倍(線性速度增加)?還是增加了n2倍(平方的速度)?還是增加了log(n)倍(對數形式增加)。

時間複雜度是乙個函式,它描述了演算法的執行時間,並且給出了執行時間與輸入規模之間的關係。

對於時間複雜度的分析,我們都是使用漸進分析,這種分析方法關注地核心就是演算法耗時會如何隨著輸入規模增長?我們使用三種記號來說明這個問題。

我們使用字母n來表示輸入的規模

符號o()表示了一種上界的感覺,也是我們常說的大o表示法,o()在這三種符號中用得最多,因為涉及到時間複雜度分析,我們經常會考慮最壞情況,因為在**的實際運用中,首先最壞情況發生的可能性還不小,其次我們需要衡量最壞情況發生的結果我們是否能接受。

它的定義也用到了極限的思想,因為漸進分析,就包含有當n趨於無窮的意思。如果學過數學分析,了解ε-δ語言,那麼一定對這塊兒的定義不會陌生。

g(n) ϵ o(f(n)): 存在常數c和n0 > 0,對所有的n > n0,我們有g(n) ≤ cf(n)。儘管定義使用的是屬於符號,因為他們是函式集合的運算,但是實際運用時我們會把屬於號記成等於,我們經常聽說的,這個演算法時間複雜度是o(n2)就來自於此。

這個符號內含的意義就是,當我規模變得很大的時候,規模再怎麼增大,我的耗時一定能被乙個函式(f(n))乘乙個常數( c )控制住。

例子:2n2+27 = o(n2): 我們找到n0 = 10,c = 3,很容易驗證對所有n > n0我們都有2n2+27 < 3n2

符號ω()表示了一種下界的感覺,和o()定義差不多,只是不等號改變了。

g(n) ϵ ω(f(n)): 存在常數c和n0 > 0,對所有的n > n0,我們有g(n) ≥ cf(n)

這個符號內含的意義就是,當我規模變得很大的時候,規模再怎麼增大,我的耗時一定比這個下界函式(f(n))乘乙個常數( c )控制住,不管怎麼樣,總存在乙個規模使得程式耗時會超過這個下界的。

例子:2n2+27 = ω(n2): 我們找到n0 = 1,c = 1,很容易驗證對所有n > n0我們都有2n2+27 > n2

符號θ()表示了一種的感覺,他包含了o()和ω(),把乙個函式上下界都固定了邊界,在n趨於很大規模的時候,形成了乙個控制帶控住了這個函式。

g(n) ϵ θ(f(n)): g(n) ϵ o(f(n))並且g(n) ϵ ω(f(n))

這個符號內含的意義就是,當我規模變得很大的時候,規模再怎麼增大,我的耗時一定比這個下界函式(f(n))乘乙個常數( c )控制住,不管怎麼樣,總存在乙個規模使得程式耗時會超過這個下界的。

例子:2n2+27 = θ(n2): 因為我們之前使用的例子表示了n2既是2n2+27的上界也是下界,找這個上下界的時候n0和c可以找的不一樣(上界對應的n0和c是10和3,下界對應的n0和c是1和1,兩者不需要統一)

符號o()表示了一種強上界的感覺,和o()定義差不多,只是條件更加嚴格了,對於所有c>0,那個上界都是上界。

g(n) ϵ o(f(n)): 存在n0 > 0,任取c>0,對所有的n > n0,我們有g(n) ≤ cf(n)。

這個符號內含的意義就是,當達到一定規模後,我的耗時永遠都超不過上界函式(f(n)),不管c怎麼幫我,c取0.01,0.000000001,耗時最後還是會比這個上界函式少,這是由於我的增長速度本質上就是慢過這個上界函式。

例子:2n2+27 = o(n3): n2的增長速度本質上就是比n3慢。我們最後找出的n0會是c的函式。

符號o()表示了一種強下界的感覺,和ω()定義差不多,只是條件更加嚴格了,對於所有c>0,那個下界都是下界。

g(n) ϵ w(f(n)): 存在n0 > 0,任取c>0,對所有的n > n0,我們有g(n) ≥ cf(n)。

這個符號內含的意義就是,當達到一定規模後,我的耗時總會超過這個下界函式(f(n)),不管c怎麼幫它,c取100,100000000,耗時最後還是會比這個下界函式多,這是由於我的增長速度本質上就是快過這個下界函式。

例子:2n2+27 = w(n): n2的增長速度本質上就是比n快。我們最後找出的n0會是c的函式。

對於演算法的耗時的公式,一般不會像上面那樣的例子直接給你乙個清楚的2n2+27這種清晰的公式。比如許多時候我們會遇到遞迴這種情況,就會出現演算法耗時的遞推公式。

例子1:選擇排序

給n個沒排好序的數,我們從這列數先選出最小的那個排最前面,然後再從剩下沒排好序的數繼續選最小的排,持續下去。這種遞迴每次問題規模減小1,所以我們有耗時的遞推公式如下:t(n)=t(n-1)+cn,我的n個數排序耗時等於我選好個最小的時間加上剩下n-1個數進行排序,從n個數里選最小的數耗時cn因為我們要瀏覽這n個數。

例子2:歸併排序

對於給n個沒排好序的數排序,我們可以把它平均拆解成兩份,每份是n/2個數,再對這n/2個數進行排序,排好後,我們對左右兩邊進行融合,這需要瀏覽這n個數才可以按順序融合好。所以耗時遞推公式如下:t(n) = t(n/2) + cn

對於這些有遞推公式的時間複雜度的計算,有如下方法可以解決:

1.猜想並用數學歸納法證明

2.迭代,比如t(n)=t(n-1)+cn,把n替換成n-1,我們也知道t(n-1) = t(n-2)+c(n-1),所以我們可以把t(n)寫開:

t(n)=t(n-1)+cn = t(n-2)+c(n-1)+ cn = … = t(1) + c*2+… + c(n-1)+cn

這樣就可把t(n)一直迭代下去,到表達成我們已知的資訊,比如t(1)

3.借助遞迴樹,比如t(n) = 2*t(n/2) + cn,我們可以把這個遞迴表達成如下樹結構:

t(n)是這棵樹所有結點值的總和,他等於根結點的值加左右子樹的所有結點值總和,而左右子樹和原樹的區別就是n變成了n/2。所以有t(n) = 2t(n/2)(這是左右子樹結點值的總和)+cn(這是根結點的值)。

注意到這樹的每一層加起來都是cn,所以t(n) = cn × 這個樹的高度。這個樹一直會分叉到1,所以高度是log2(cn),因為高度表示的就是cn需要分叉幾次到1,就是對cn求對數。

所以t(n) = cn × log2(cn),t(n) = θ(nlog2n)

更一般的:t(n) = at(n/b) + cnk, t(1) = c,

a ≥ 1, b > 1, c > 0 and k ≥ 0

我們也可以畫出如下遞迴樹後算出t(n)的值

同樣也是每行相加都呈現乙個規律性的值,再對t(n)進行分析,最後有如下結論:

對於t(n) = at(n/b) + cnk, t(1) = c,

a ≥ 1, b > 1, c > 0 and k ≥ 0

有:1.當a < bk, t(n) = θ(nk)

2.當a = bk, t(n) = θ(nklogn)

3.當a > bk, t(n) = θ(n^logba)

1.我們首先談及了時間複雜度的定義,我們最關心的就是演算法的耗時增長的速率,是呈什麼函式形式增長的。

2.接下來我們通過符號,將時間複雜度的表示進行了量化,包括給了上界,下界,緊,強上界,強下界的這些類似概念,很好地區刻畫了乙個演算法的時間隨著規模增長的形式和規律。

3.我們對演算法的時間複雜度計算方法進行了乙個初步的介紹,並對遞迴的演算法給出了乙個綜合性的公式。

時間複雜度和大O記號

時間複雜度 執行乙個程式的基本步驟的總數量。這麼理解比較直觀吧,看他一共需要執行的步驟 q a,b,c,是1000以內的自然數,滿足a b c 1000,a平方加b平方等於c平方。方法一 列舉法 import time start time time.time for a in range 1001...

幾種排序以及其時間複雜度

常用的排序演算法的時間複雜度和空間複雜度 排序法 最差時間分析 平均時間複雜度 穩定度 空間複雜度 氣泡排序 o n2 o n2 穩定 o 1 快速排序 o n2 o n log2n 不穩定 o log2n o n 選擇排序 o n2 o n2 穩定 o 1 二叉樹排序 o n2 o n log2n...

幾種排序以及其時間複雜度

1.選擇排序 不穩定,時間複雜度 o n 2 選擇排序的基本思想是對待排序的記錄序列進行n 1遍的處理,第i遍處理是將l i.n 中最小者與l i 交換位置。這樣,經過i遍處理之後,前i個記錄的位置已經是正確的了。2.插入排序 穩定,時間複雜度 o n 2 插入排序的基本思想是,經過i 1遍處理後,...