Fibonacci数列的log(n)解法

对于Fibonacci数列,每一项等于前两项的和 1,1,2,3,5,8,13,21..., 可以用公式表示为fb(n)=fb(n-1)+fb(n-2), 这个问题如果用最简单的递归来做,时间复杂度是指数,如果用动态规划记住每一步递归算出来的值,则时间复杂度为O(n). 但是有一种更简单的方法,可以将时间复杂度降到O(log(n)). 

首先,Fibonacci数列可以表示成矩阵相乘的形式

      那么进一步可以推出:,如果直接计算,那么时间复杂度仍然是O(n),但是此处的,由于其是不断重复计算,所以有优化的空间。为了方便起见,使用a代替

Fibonacci数列的log(n)解法_第1张图片

整个结构就类似于一棵树,每个父节点都等于两个子节点的积,而两个子节点的值是相同的,所以其实只用计算一次,每一层要做的操作就是把从下一层得到的值平方一下,然后继续传递给更上一层。而整个这个结构有log(n)+1层,也就是说只用进行log(n)+1次计算就可以得到的值,所以对于只要进行log(n-2)+1次计算即可,当然每次的矩阵相乘运算实际上包含了多次的矩阵元素的相乘和相加,但这只是将log(n-2)+1乘以一个常数,总的时间复杂度仍为O(log(n)). 在得到后,在做一步计算即可,将fb(1)和fb(2)组成的向量和它相乘,得到的向量的第一个元素就是fb(n)。需要注意的一点是,对于n是奇数,不能直接除以二,我们先将n减1,得到偶数n-1后,再用n-1进行二叉细分,待得到的结果后,再乘以一个a,就可以得到的值。具体的实现代码如下:

#include

struct Vector2
{
	long long x, y;
};

class Matrix
{
public:
	long long m00,m01,m10,m11;
	Matrix(long long m_00, long long m_01, long long m_10, long long m_11);
	Matrix();
	Vector2 operator * (Vector2 v);
	Matrix operator * (Matrix m);

};

Matrix::Matrix()
{
	m00 = 0;
	m01 = 0;
	m10 = 0;
	m11 = 0;
}

Matrix::Matrix(long long m_00, long long m_01, long long m_10, long long  m_11)
{
	m00 = m_00;
	m01 = m_01;
	m10 = m_10;
	m11 = m_11;
}

Matrix Matrix::operator * (Matrix m)
{
	Matrix result;
	result.m00 = m00*m.m00+m01*m.m10;
	result.m01 = m00*m.m01+m01*m.m11;
	result.m10 = m10*m.m00+m11*m.m10;
	result.m11 = m10*m.m01+m11*m.m11;
	
	return result;
}

Vector2 Matrix::operator * (Vector2 v)
{
	Vector2 result;
	result.x = m00*v.x +m01*v.y;
	result.y = m10*v.x+m11*v.y;

	return result;
}

Matrix Fibonacci(int n)
{
	Matrix r;
	Matrix m(1,1,1,0);

	if(n==1)
	{
		return m;
	}
	else
	{
		if(n%2==0)
		{
			r=Fibonacci(n/2);
			r=r*r;
		}
		else 
		{
			r=Fibonacci((n-1)/2);
			r=r*r;
			r=r*m;
		}
	}
	return r;
}

int main()
{
	int n;
	Vector2 v;
	v.x=1; 
	v.y=1;
	scanf("%d", &n);
	Matrix m= Fibonacci(n-2);
	v = m*v;

	printf("%lld\n", v.x);
	return 1;
}

除了使用矩阵的方法,直接利用Fibonacci数列的公式也是一种方法: Fibonacci数列的log(n)解法_第2张图片,计算方法同矩阵方法相同,同样通过二叉细分的方式,时间复杂度同样为O(logn)

你可能感兴趣的:(Fibonacci数列的log(n)解法)