斐波拉契数列的N中求法

版权声明:转载请注明出处 https://blog.csdn.net/qq_41431457/article/details/88824199

No.1 递归版

lom recur(lom x)
{
	if(n<=1) return x;
	else return recur(x-1)+recur(x-2);
}

简洁明了,一气呵成,将递归的精髓用的淋漓尽致,但是你求一个第一万个数试试,保证很好玩,嗯,递归栈也是有限的,默认栈空间大小只有8M。一般来说,8M的栈空间对于一般程序完全足够。如果8M的栈空间不够使用,那么就需要重新审视你的代码设计了。

No.2 递归1.0版

lom a[10000000];
lom recur(lom x)
{
	if(n < 2)    return n;
    else return a[n]=recur(n-1)+a[n-2];
}

用数组保存已经求过的值,but,终究还是递归,虽然时间复杂度降到了O(n),但递归栈还是很深

No.3送代版

	a[1]=a[2]=1;
	for(lom i=3;i<=10000;i++)
	a[i]=a[i-1]+a[i-2];

这个不解释


重点来了啊,扶稳坐好了

No.n-1矩阵快速幂版:

#include<bits/stdc++.h>
#define lom unsigned long long
using namespace std;
void matrixmuti(lom A[2][2],lom B[2][2],lom C[2][2])
{
	lom temp[2][2]; //临时存储,参数的传递 
	memset(temp,0,sizeof(temp));
	//矩阵求法 
	temp[0][0]=A[0][0]*B[0][0] + A[0][1]*B[1][0];
	temp[0][1]=A[0][0]*B[0][1] + A[0][1]*B[1][1];
	temp[1][0]=A[1][0]*B[0][0] + A[1][1]*B[1][0];
	temp[1][1]=A[1][0]*B[0][1] + A[1][1]*B[1][1];
	
	memcpy(C,temp,sizeof(temp));//copy 
}
lom feibo(lom n)
{
	if(n<=1) return n;
	lom ans[2][2]={1,0,0,1};//结果矩阵 
	lom   A[2][2]={1,1,1,0};//单位矩阵 
	while(n>0)//快速幂 
	{
		if(n&1) matrixmuti(A,ans,ans);
		n>>=1;
		matrixmuti(A,A,A);
	}
	return ans[0][1];
}
int main()
{
	cout<<feibo(10);
	return 0; 
}

时间复杂度降到O(log n),

没学过线性代数的童鞋看起来会很难理解,不过没关系,去百度一下矩阵乘法就行,还有快速幂,其实也是一种常见的算法,不懂快速幂的可以用笔在纸上模拟一下,快速幂:https://blog.csdn.net/qq_41431457/article/details/88355867

看一波讲解:

矩阵快速幂解法是一种高效的解法,需要推导,对此不感兴趣的可直接看最终推导结果。学过数学的都知道,下面的式子成立是显而易见的,不多做解释。
a^n= \begin{cases} a^{n \over 2} \cdot a^{n \over 2}, & \text {n为偶数} \\ a\cdot a^{n \over 2} \cdot a^{n \over 2}, & \text{n为奇数} \end{cases}

如果a为矩阵,等式同样成立,后面我们会用到它。
假设有矩阵2*2矩阵A,满足下面的等式:

A * \begin{bmatrix} f(n-1) \\ f(n-2)\end{bmatrix} = \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix} = \begin{bmatrix} f(n-1)+f(n-2) \\ f(n-1)\end{bmatrix}

可以得到矩阵A:

A = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}

因此也就可以得到下面的矩阵等式:

\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} * \begin{bmatrix} f(n-1) \\ f(n-2)\end{bmatrix} = \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix}

再进行变换如下:

\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} *\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} * \begin{bmatrix} f(n-2) \\ f(n-3)\end{bmatrix} = \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix}

以此类推,得到:

\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} ^{n-1} * \begin{bmatrix} f(1) \\ f(0)\end{bmatrix} = \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix}

实际上f(n)就是矩阵 A^{n-1} 中的A[0][0],或者是矩阵 A^{n} 中的A[0][1]。

那么现在的问题就归结为,如何求解 A^n ,其中A为2*2的矩阵。

该算法的关键部分在于对 A^n 的计算,它利用了我们开始提到的等式,对奇数和偶数分别处理。假设n为9,初始矩阵为E则计算过程如下:

  • 9为奇数,则计算E*A,随后A变为A*A,n变为9/2,即为4
  • 4为偶数,则结果仍为E*A,随后A变为 (A^2)*(A^2)=A^4 ,n变为4/2,即2
  • 2为偶数,则结果仍未E*A,随后变A变为 (A^4)*(A^4)=A^8 ,  n变为2/2,即1
  • 1为奇数,则结果为E*(A^8)*A

系好安全带啊,飞机起飞了啊

No.N终极通项公式版

lom slove(lom n)
{
	double a=(1.0+sqrt(5.0))/2.0;
	double b=(1.0-sqrt(5.0))/2.0;
	a=pow(a,n);
	b=pow(b,n);
	return (lom)((a-b)/sqrt(5.0)+0.5);
}

公式:

f(n) = \frac{({\frac{1+\sqrt5}{2}})^n-({\frac{1-\sqrt5}{2}})^n}{\sqrt5}

是不是看起来非常简单,像发现了新大陆,但是我告诉你,这个只能求前76项,为什么?因为涉及到无理数,还不能取余,double都不够存,除非你开一个非常大的数组模拟小数,大到经过 以long long的最大为指数次幂运算后,丢失的精度还可以忽略不计

总的来说,还是矩阵快速幂好用 首推O.N-1

猜你喜欢

转载自blog.csdn.net/qq_41431457/article/details/88824199