算法习题19:Fibonacci数列深入

题目:定义Fibonacci数列如下:   
  / 0 n=0
f(n)= 1 n=1
  \ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项

——————————————————————————————

这个题目大家很熟悉,就是Fibonacci数列,大家第一个想到的就是递归方法,是的,这个我们学数据结构算法的时候用的就是这个方法,代码精简漂亮,可是有没有想过时间和空间上的开销呢?首先这是一个递归,空间上开销不用说,栈会不断积累,时间,当我们计算F(n-1)时候计算了F(n-2),可是递归没有记忆功能,所以计算F(n-2)还是在来一次,这就造成了时间复杂度的上升应该是O(n2)

(1)循环 

所以这里稍微改进下,首先,递归一般都可以采用非递归方式实现,这里我们用循环来实现

long Fibonacci1(long input){
	long a0 = 1;
	long a1 = 1;
	long sum = 0;
	int i = 0;
	if(input <2)
		return 1;
	for(i=2;i<=input;i++){
		sum = a0+a1;
		a0 = a1;
		a1 = sum;
	}
	return sum;
}

其实代码不难,重要的是思路把,

后来我上网搜了下大家对这道题的看法,有人提出了更巧妙的方式,循环的时间复杂度我们知道就是O(N)似乎已经很好了,不过还有O(logN)的方法

(2)矩阵

大家可以计算下

| 1  1 |

| 1  0 |

这个矩阵的n次方的规律,我们发现,刚好就是

|f(n-1)+f(n-2)    f(n-1) |

|f(n-1)               f(n-2) |

所以这道题就可以转换成矩阵求幂了。

可以A^n还是需要计算n次啊,我们可以利用平方的性质  [A^(n/2)}^2 = A^n  所以以此类推 那就是logN的复杂度了 不过要注意奇数偶数的幂

偶数    A^n = [A^(n/2)}^2

奇数    A^n = [A^((n-1)/2)}^2*{1,1,1,0}

long Fibonacci3(int input){
	long arr[4] = {1,1,1,0};
	long* result = F3(arr, input);
	return result[0];
}

long* F3(long* arr, int input){
	long arr1[4], arr2[4];
	long* temp1 = arr1;
	long* temp2 = arr2;
	if(input == 1){
		temp1[0] = 1;
		temp1[1] = 1;
		temp1[2] = 1;
		temp1[3] = 0;
		return temp1;
	}
	if(input == 2){//求得平方
		temp1[0] = arr[0]*arr[0] + arr[1]*arr[2];
		temp1[1] = arr[0]*arr[1] + arr[1]*arr[3];
		temp1[2] = arr[2]*arr[0] + arr[3]*arr[2];
		temp1[3] = arr[2]*arr[1] + arr[3]*arr[3];
		return temp1;
	}else{
		if(input%2 == 0){
			temp1 = F3(arr, input/2);
			temp2[0] = temp1[0]*temp1[0] + temp1[1]*temp1[2];
			temp2[1] = temp1[0]*temp1[1] + temp1[1]*temp1[3];
			temp2[2] = temp1[2]*temp1[0] + temp1[3]*temp1[2];
			temp2[3] = temp1[2]*temp1[1] + temp1[3]*temp1[3];
			return temp2;
		}else{
			temp1 = F3(arr, (input-1)/2);
			temp2[0] = temp1[0]*temp1[0] + temp1[1]*temp1[2];
			temp2[1] = temp1[0]*temp1[1] + temp1[1]*temp1[3];
			temp2[2] = temp1[2]*temp1[0] + temp1[3]*temp1[2];
			temp2[3] = temp1[2]*temp1[1] + temp1[3]*temp1[3];

			temp1[0] = temp2[0] + temp2[1];
			temp1[1] = temp2[0] ;
			temp1[2] = temp2[2] + temp2[3];
			temp1[3] = temp2[2] ;
			return temp1;
		}
	}
	cout<<"NULL??";
	return NULL;
}


虽然这种方法依然是一个递归,当时次数从n^2---->logn  这个是一个很大的进步

参见:http://blog.csdn.net/yuucyf/article/details/6625301
(3)递归

为了比较,这里还是把老式方法给出来

long Fibonacci2(long input){
	if(input < 2)
		return 1;
	else
		return Fibonacci2(input-1)+Fibonacci2(input-2);
}


我这里为了不让数列数太大,我循环执行,为了比较效率

long Fibonacci1(long input);
long Fibonacci2(long input);
long Fibonacci3(int input);
long* F3(long* arr, int input);

int main() {
	long input = 0l;
	cin>>input;

	int i =0;
	long result;
	clock_t start,end;
        //循环方法
	cout<<endl<<"循环:"<<endl;
	start = clock();
	for(i=0;i<3000000;i++)
		result = Fibonacci1(input);
	cout<<result;
	end = clock();
	cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;
        //老式递归
	cout<<endl<<"递归:"<<endl;
	start = clock();
	for(i=0;i<3000000;i++)
		result = Fibonacci2(input);
	cout<<result;
	end = clock();
	cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;
        //举证方法
	cout<<endl<<"矩阵:"<<endl;
		start = clock();
		for(i=0;i<3000000;i++)
			result = Fibonacci3(input);
		cout<<result;
		end = clock();
		cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;

	return 0;
}


输出比较

13

循环:
377
0.18s
递归:
377
9.68s
矩阵:
377
0.26s
我们看到,老式的递归将近10秒了,由于输入10较小所以矩阵的速度无法充分体现出来,这里递归影响较大

我们看到,当输入求50的时候,时间就差开啦

50

循环:
20365011074
0.71s
矩阵:
20365011074
0.38s



你可能感兴趣的:(递归,时间复杂度,fibonacci,改进,费伯那奇数列)