從一些簡單的例子看算法時間複雜度

從一些簡單的例子看算法時間複雜度

    在編程中,一段代碼的執行效率實際上很難估算和預測,其主要受到以下幾個方面的影響:javascript

1.算法依據的數學基礎。java

2.編譯器產生的代碼質量和語言的執行效率。算法

3.問題的輸入規模。編程

4.硬件的執行速度。函數

一般狀況下,問題的輸入規模和算法的數學基礎是編碼人員須要考慮的條件。時間複雜度是一個用來描述算法執行效率的重要標準。  性能

    在理解時間複雜度以前,你應該先了解什麼叫作算法的時間頻度,所謂時間頻度便是一個算法解決問題所消耗的時間。可是通常狀況下,一個算法解決問題消耗的時間一般與輸入值有關,例如咱們輸入一個整數,找到比它小的全部正偶數,代碼以下:編碼

let n = 10;

for (var i = 0; i < n; i++) {
	if (i%2==0) {
		console.log(i);
	}
}

上面代碼,當輸入n爲10時,循環會執行10次,若是時間頻度t,則當輸入n爲20時,時間頻度爲2t。時間複雜度是用來描述隨着問題規模n的變化時間頻度t的變化規律。下面是一段更加數學風格的描述:spa

 通常狀況下,算法中基本操做重複執行的次數是問題規模n的某個函數,用T(n)表示,如有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值爲不等於零的常數,則稱f(n)是T(n)的同數量級函數。記做T(n)=O(f(n)),O(f(n)) 爲算法的漸進時間複雜度,簡稱時間複雜度。code

    計算一個算法的時間複雜度時,咱們能夠將算法分解爲逐條語句,計算每條語句的時間複雜度後再進行累加,以下代碼的做用是對輸入進行求累加:遞歸

let n = 10;  
let res = 0; //1
for (var i = n; i > 0; i--) { //1+(n+1)+(n+1)
	res = i+res;  //n
}
console.log(res);//1

當n輸入爲10時,時間頻度爲1+1+n+1+n+1+n+1 = 3n+5。設算法的時間複雜度函數爲f(n),(3n+5)/f(n)當n趨於無窮大時,上式能夠簡化爲3n/f(n),取f(n)=n,上次結果爲非零常數,所以此算法的時間複雜度爲f(n)=n,記作O(n)。

    當算法的執行時間頻度和n無關時,算法的時間複雜度爲O(1),這是時間複雜度最小的函數,可是須要注意,時間複雜度小並不能說明算法執行耗費的時間短,好比一萬行代碼每行只執行一次的算法時間複雜度也爲O(1)。

     常見的算法時間複雜度由小到大以此爲:

Ο(1)<Ο(log²n)<Ο(n)<Ο(nlog²n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)

其中O(log² n)是除了O(1)外時間複雜度最小的函數,例如以下代碼:

let n = 10;  
var i = 1;

while(i<n){//2^t<n t<log2(n)  t爲時間頻度
	i = i * 2;
	tip++;
}

上面的時間頻度爲 1+1+log2(n)+log2(n)+log2(n),去掉常數項後爲3log2(n),時間複雜度爲O(log2(n))。若是將上面的代碼在加一層循環,則時間複雜度會變爲O(nlog3(n)):

let n = 10;  


for (var i = 0; i < n; i++) {// 1+n+1+n+1
	var j = 1;   //n
	while(j<n){//[3^t<n t<log3(n)]n  t爲時間頻度
		j = j*3;
	}
}

    經過上面的示例,也很容易能夠看出,循環層數的增多會劇烈的增長算法的時間複雜度,若是在遞歸函數中使用循環,則很容易產生時間複雜度爲O(n!)的代碼,從數學上看,這種代碼隨着輸入複雜度的增長性能會急劇降低,在使用遞歸加循環時,仍是要多多注意,示例代碼以下:

function func(n) {  //n
	if (n<0) {
		return;
	}
	var i = 0;    //n
	for (; i < n; i++) { //n*(n-1)*(n-2)...*1
		console.log("tip");
	}
	func(--i);
}
func(10);

上面示例的JavaScript代碼當傳入n爲150時的耗時已經和正常循環10000次的相同。

相關文章
相關標籤/搜索