1. 程式人生 > >演算法效能指標的衡量

演算法效能指標的衡量

1. 時間複雜度

     概念:時間複雜度實際上就是一個函式,用來計算一個演算法執行基本操作的次數。基本操作顧名思義,即就是一個演算法的最基本的運算,如:計算1+2+...+100,它的基本操作為’+’。

  時間複雜度的計算:在實際操作中我們通常關注的是一個演算法的最壞執行情況,即對於任意輸入規模N,演算法執行的最長時間。一般情況下使用O漸進法來計算。

    f(N):N為演算法規模,f(N)為演算法時間複雜度表達函式,則該演算法的時間複雜度用O漸進表示法可表示為O(f(N)),此時的f(N)是表達函式中增長最快的一項。常數型增長可表示為O(1)1並不是表示一次,而是表示常數次,忽略常數及常係數。

    eg:氣泡排序:f(n)=(n-1)+(n-2)+...+1=(n-1)(n-2)/2

   由氣泡排序的時間複雜度表達函式可得用O漸進表示法可表示氣泡排序的時間複雜度為O(n^2)

2. 空間複雜度

  概念:一個表示演算法開闢空間大小的函式,表示:O(f(N))f(N)為演算法的空間複雜度表達函式O(1)則表示演算法每執行一次開闢常數個空間。

  遞迴呼叫的空間複雜度 = 遞迴的深度 * 每次遞迴開闢的空間大小

     eg: 斐波那契數列:1123581321...

    每次遞迴開闢常數個空間,遞迴N次,其空間複雜度為O(N)

3. 例項分析

 1.二分查詢(升序陣列中查詢元素x,找到了返回該元素下標,未找到返回-1)

二分查詢的的遞迴及非遞迴實現如下:

#include<stdio.h>
#include<windows.h>
//遞迴演算法
int binarySearch1(int *a,int key,int len)
{
	int end = len-1,start = 0,mid = 0;
	
	while(start <= end)
	{
		mid=(end+start)/2;
		if(key<a[mid])
		{
			end = mid - 1;
		}
		else if(key>a[mid])
		{
			start = mid + 1;
		}
		else
		{
			return mid;
		}
	}
	return -1;
}
//非遞迴演算法
int binarySearch2(int *arr,int key,int a,int b)
{
	int mid = 0;
	if(a>b)
	{
		return -1;
	}
	else
	{
		mid = (a+b)/2;
		if(arr[mid]==key)
		{
			return mid;
		}
		else if(arr[mid]<key)
		{
			search2(arr,key,mid+1,b);
		}
		else
		{
			search2(arr,key,a,mid-1);	
		}
	}
}
int main()
{
	int a[10]={1,2,3,4,5,6,7,8,9};
	int len = sizeof(a)/sizeof(a[0]);
	printf("%d\n",binarySearch1(a,5,len));
	printf("%d\n",binarySearch2(a,2,0,len-1));
	system("pause");
	return 0;
}

在二分查詢演算法中,若用x表示查詢次數,n表示陣列中元素個數,則x和n滿足表示式2^x=n,即二分查詢的時間複雜度函式為x=log2(n);時間複雜度為O(log(n))。其中遞迴演算法的空間複雜度為O(log(n)),非遞迴空間複雜度為O(1)。

(2)斐波那契數列(從第三項開始後一項等於前兩項之和)

  程式碼實現如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>

//遞迴
int Fibonaci1(int input)
{
	if((input==1)||(input==2))
	{
		return 1;
	}
	return Fibonaci(input-1)+Fibonaci(input-2);
}
//非遞迴
int Fibonaci(int input)
{
	int input1 = 1;
	int input2 = 1;
	int result = 0;
	if((input == 1)||(input == 2))
	{
		return 1;
	}
	while(input>2)
	{
			result=input1+input2;
			input1=input2;
			input2=result;
			input--;
	}
	
	return result;
		
}
int main()
{
	int input = 0;
	scanf("%d",&input);
	printf("非遞迴輸出第%d個Fibonacci數為:%d\n",input,Fibonaci(input));
	printf("  遞迴輸出第%d個Fibonacci數為:%d\n",input,Fibonaci1(input));
	system("pause");
	return 0;
}
        斐波那契數列演算法的時間複雜度計算:演算法規模:n,則有時間複雜度函式f(n)=2^n-a(常數),故有 斐波那契數列的時間複雜度為O(2^n)。遞迴演算法中每次開闢常數個空間,遞迴深度為n,所以遞迴演算法的空間複雜度為O(n),非遞迴演算法的空間複雜度為O(1)。

  4.尾遞迴

       概念:函式返回之前的最後一個操作若是遞迴呼叫,則該函式進行了尾遞迴。

  優點:在遞迴呼叫中,系統不斷的進行壓棧、退棧操作,一旦處理的資料過多,很有可能會導致棧溢位,但尾遞迴是在呼叫之前已將所有計算任務完成,無需有其他儲存,只需將得到的結果交給子函式即可,因此子函式可以利用當前棧幀,將原先資料覆蓋即可,運算相對一般遞迴更節省空間,防止了棧溢位,減少了建立棧幀時的開銷。