1.決定因素
1.1算法選用的策略
1.2問題的規模
1.3編寫程序的語言
1.4編譯程序產生的機器代碼的質量
1.5計算機執行指令的速度
2.其他影響元素
3.問題的規模(時間復雜度)
3.1定義
一個特定算法的“運行工作量”的大小,只依賴於問題的規模(通常用整數量n表示),或者說,它是問題規模的函數。
假如,隨着問題規模 n 的增長,算法執行時間的增長率和 f(n) 的增長率相同,則可記作:T (n) = O(f(n)), 稱T (n) 為算法的(漸近)時間復雜度。
一個算法是由控制結構(順序、分支和循環3種)和原操作(指固有數據類型的操作)構成的,算法的運行時間取決於兩者的綜合效果。
3.2估算算法的時間復雜度
(Time Complexity)
3.2.1定義
從算法中選取一種對於所研究的問題來說是基本操作的原操作,以該基本操作 在算法中重復執行的次數 作為算法運行時間的衡量准則。
“基本操作” 指的是,該操作重復執行次數和算法的運行時間成正比。
算法的執行時間=∑原操作(i)的執行次數×原操作(i)的執行時間
詳細算法的執行時間與原操作執行次數之和成正比
算法= 控制結構+ 原操作(固有數據類型的操作)
1兩個矩陣相乘 eg1:兩個矩陣相乘 void mult(inta[], int b[], int&c[] ) { // 以二維數組存儲矩陣元素,c為 a 和 b的乘積 for (i=1; i<=n; ++i){ for(j=1; j<=n; ++j) { c[i,j] = 0; for(k=1; k<=n; ++k) c[i,j] += a[i,k]*b[k,j]; } } } 基本操作: 乘法操作時間復雜度: O(n^3)
2選擇排序
3起泡排序
4有如下遞歸函數fact(n),分析其時間復雜度 int fact(int n){ if(n<=1) return(1); (1) else return(n*fact(n-1)); (2) } 解:設fact(n)的運行時間復雜函數是T(n), 該函數中語句(1)的運行時間是O(1), 語句(2)的運行時間為:T(n-1)+O(1), 其中O(1)為基本運算時間, 因此: T(n) = O(1)+T(n-1) = O(1)+O(1)+T(n-2) = …… = (n-1)*O(1)+T(1) = n*O(1) = O(n)則fact(n)的時間復雜度為O(n)。
3.2.2分析算法時間復雜度的一般步驟
3.2.3漸進符號
設n為算法中的問題規模,通常用大O、大Ω和Θ等三種漸進符號表示算法的執行時間與n之間的一種增長關系。
3.2.3.1 大O符號
定義
定義1(大O符號),f(n)=O(g(n))(讀作“f(n)是g(n)的大O”)當且僅當存在正常量c和n0,使當n≥n0時,f(n)≤cg(n),即g(n)為f(n)的上界。
如3n+2=O(n),因為當n≥2時,3n+2≤4n。
10n2+4n+2=O(n4),因為當n≥2時,10n2+4n+2≤10n4。
大O符號用來描述增長率的上界,表示f(n)的增長最多像g(n) 增長的那樣快,也就是說,當輸入規模為n時,算法消耗時間的最大值。這個上界的階越低,結果就越有價值,所以,對於10n2+4n+2,O(n2)比O(n4) 有價值。
一個算法的時間用大O符號表示時,總是采用最有價值的g(n)表示,稱之為“緊湊上界”或“緊確上界”。
一般地,如果有
常用的幾種時間復雜度的關系
說明:
1.在難以精確計算基本操作執行次數(或語句頻度)的情況下,只需求出它關於n的增長率或階即可2.一個算法的時間復雜度可以具體分為最好、最差(又稱最壞)和平均三種情況討論。
除特別說明外,正常均指最壞情況下的時間復雜度。
例子
1兩個矩陣相乘
eg1:兩個矩陣相乘
void mult(inta[], int b[], int&c[] ) { // 以二維數組存儲矩陣元素,c為 a 和 b的乘積 for (i=1; i<=n; ++i){ for(j=1; j<=n; ++j) { c[i,j] = 0; for(k=1; k<=n; ++k) c[i,j] += a[i,k]*b[k,j]; } } } 基本操作: 乘法操作時間復雜度: O(n^3)
2選擇排序
3起泡排序
4有如下遞歸函數fact(n),分析其時間復雜度
int fact(int n){ if(n<=1) return(1); (1) else return(n*fact(n-1)); (2) } 解:設fact(n)的運行時間復雜函數是T(n), 該函數中語句(1)的運行時間是O(1), 語句(2)的運行時間為:T(n-1)+O(1), 其中O(1)為基本運算時間, 因此: T(n) = O(1)+T(n-1) = O(1)+O(1)+T(n-2) = …… = (n-1)*O(1)+T(1) = n*O(1) = O(n)則fact(n)的時間復雜度為O(n)。
3.2.3.2 大Ω符號
定義2(大Ω符號),f(n)= Ω(g(n))(讀作“f(n)是g(n)的大Ω”)當且僅當存在正常量c和nθ,使當n≥n0時,f(n)≥cg(n),即g(n)為f(n)的下界。
如3n+2=Ω(n),因為當n≥1時,3n+2≥3n。
10n2+4n+2=Ω(n2),因為當n≥1時,10n2+4n+2≥n2。
大Ω符號用來描述增長率的下界,表示f(n)的增長最少像g(n) 增長的那樣快,也就是說,當輸入規模為n時,算法消耗時間的最小值。
與大O符號對稱,這個下界的階越高,結果就越有價值,所以,對於10n2+4n+2,Ω(n2)比Ω(n) 有價值。一個算法的時間用大Ω符號表示時,總是采用最有價值的g(n)表示,稱之為“緊湊下界”或“緊確下界”。
一般地,如果,有
。
3.2.3.3大Θ符號
定義3(大Θ符號),f(n)= Θ(g(n))(讀作“f(n)是g(n)的大Θ”)當且僅當存在正常量c1、c2和n0,使當n≥n0時,有c1g(n)≤f(n)≤c2g(n),即g(n)與f(n)的同階。
如3n+2=Θ (n),10n2+4n+2=Θ(n2)。
一般地,如果,有f(n)=Θ(nm)。
大Θ符號比大O符號和大Ω符號都精確,f(n)=Θ(g(n),當且僅當g(n)既是f(n)的上界又是f(n)的下界。
3.2.3.4關系
3.3算法的最好、最壞和平均情況
設一個算法的輸入規模為n,Dn是所有輸入的集合,任一輸入I∈Dn,P(I)是I出現的概率,有ΣP(I) =1,T(I)是算法在輸入I下所執行的基本語句次數,則該算法的平均執行時間為:A(n)=。
也就是說算法的平均情況是指用各種特定輸入下的基本語句執行次數的帶權平均值。
算法的最好情況為:G(n)=,是指算法在所有輸入I下所執行基本語句的最少次數。
算法的最壞情況為:W(n)=,是指算法在所有輸入I下所執行基本語句的最大次數。
4.算法選用的策略
4.3非遞歸算法的時間復雜度分析
對於非遞歸算法,分析其時間復雜度相對比較簡單,關鍵是求出代表算法執行時間的表達式。
通常是算法中基本語句的執行次數,是一個關於問題規模n的表達式,然后用漸進符號來表示這個表達式即得到算法的時間復雜度。
【例1.6】給出以下算法的時間復雜度。 void func(int n) { int i=1,k=100; while (i<=n) { k++; i+=2; } } 解:算法中基本語句是while循環內的語句。
設while循環語句執行的次數為m,i從1開始遞增,最后取值為1+2m,有: i=1+2m≤n f(n)=m≤(n-1)/2=O(n)。 該算法的時間復雜度為O(n)。
4.2遞歸算法的時間復雜度分析
遞歸算法是采用一種分而治之的方法,把一個“大問題”分解為若干個相似的“小問題”來求解。
對遞歸算法時間復雜度的分析,關鍵是根據遞歸過程建立遞推關系式,然后求解這個遞推關系式,得到一個表示算法執行時間的表達式,最后用漸進符號來表示這個表達式即得到算法的時間復雜度。
【例1.7】有以下遞歸算法: void mergesort(int a[],int i,int j) { int m; if (i!=j) { m=(i+j)/2; mergesort(a,i,m); mergesort(a,m+1,j); merge(a,i,j,m); } } 其中,mergesort()用於數組a[0..n-1](設n=2k,這里的k為正整數)的歸並排序,
調用該算法的方式為: mergesort(a,0,n-1); 另外merge(a,i,j,m)用於兩個有序子序列a[i..j]和a[j+1..m]的有序合並,
是非遞歸函數,它的時間復雜度為O(n)(這里n=j-i+1)。分析上述調用的時間復雜度。
解:設調用mergesort(a,0,n-1)的執行時間為T(n),
由其執行過程得到以下求執行時間的遞歸關系(遞推關系式):
T(n)=O(1) 當n=1
T(n)=2T(n/2)+O(n) 當n>1
其中,O(n)為merge()所需的時間,設為cn(c為正常量)。因此:
T(n) = 2T(n/2)+cn=2[2T(n/22)+cn/2]+cn=22T(n/22)+2cn
= 23T(n/23)+3cn
= …
= 2kT(n/2k)+kcn
= nO(1)+cnlog2n=n+cnlog2n //這里假設n=2k,則k=log2n
= O(nlog2n)
【例1.8】求解梵塔問題的遞歸算法如下,分析其時間復雜度。 void Hanoi(int n,char x,char y,char z) { if (n==1) printf("將盤片%d從%c搬到%c\n",n,x,z); else { Hanoi(n-1,x,z,y); printf("將盤片%d從%c搬到%c\n",n,x,z); Hanoi(n-1,y,x,z); } }
解:設調用Hanoi(n,x,y,z)的執行時間為T(n),由其執行過程得到以下求執行時間的遞歸關系(遞推關系式):
T(n)=O(1) 當n=1
T(n)=2T(n-1)+1 當n>1
T(n) = 2[2T(n-2)+1]+1=22T(n-2)+1+21
= 23T(n-3)+1+21+22
= …
= 2n-1T(1)+1+21+22+…+2n-2
= 2n-1 = O(2n)