每天学习一算法系列(19)(输入n,用最快的方法求该数列的第n 项)

 

题目:

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

思路一:

我相信很多程序员看到这道题都很容易想到用递归的方法解这个问题,但是,你知道的,这并不是最快的方法。还有一种方法就是用一个For(While)循环来遍历同样可以解这个问题,而且时间复杂度为O(n),相对于递归的方法还好点,下面先给出这两种方法的代码。

 

代码如下:

/*------------------------------------
求出Fibonacci数列的第N项值.
Copyright by yuucyf.		2011.07.22
-------------------------------------*/
long long FibNItemValue(int i32NItem)
{
	if (i32NItem <= 0)	return 0;
	if (i32NItem >= 1 && i32NItem <= 2)	return 1;

	return FibNItemValue(i32NItem - 1) + FibNItemValue(i32NItem - 2);
}


long long FibNItemValue_2(int i32NItem)
{
	long long int llFirst = 1, llSecond = 1, llNItemValue = 0;
	if (i32NItem <= 0)	return 0;
	if (i32NItem >= 1 && i32NItem <= 2)	return 1;

	for (int i32I = 3; i32I <= i32NItem; i32I++)
	{
		llNItemValue = llFirst + llSecond;
		llFirst = llSecond;
		llSecond = llNItemValue;
	}

	return llNItemValue;
}


int _tmain(int argc, _TCHAR* argv[])
{
	_tprintf(_T("N item value is %u.\n"), FibNItemValue(40));

	return 0;
}


思路二:

以上两种方法都可以解这道问题,但是并不是最快的,下面介绍一种时间复杂度是O(logn)的方法,在介绍这种方法之前,先介绍一个数学公式:

{ f(n),  f(n-1), f(n-1),  f(n-2) }  = {1, 1, 1, 0}n-1

(提示:{ f(n+1), f(n), f(n), f(n-1) } 表示一个矩阵,在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1). )

有了这个公式,要f(n)的值,我们只需要求得矩阵{1,  1,  1, 0}的n-1次方,因为矩阵{1,  1,  1, 0} 的n-1次方的结果的第一行第一列就是f(n)。这个数学公式用数学归纳法不难证明,感兴趣的朋友不妨自己证明一下。现在的问题转换为求矩阵{1,  1,  1,  0}的乘方,如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:

        /  an/2*an/2                      n为偶数时
an=
        \  a(n-1)/2*a(n-1)/2            n为奇数时

要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下,如果把求n次方的问题看成一个大问题,把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。

实现这种方式时,首先需要定义一个2×2的矩阵,并且定义好矩阵的乘法以及乘方运算。当这些运算定义好了之后,剩下的事情就变得非常简单。

 

#include "stdafx.h"
#include <assert.h>

/*------------------------------------
求出Fibonacci数列的第N项值.
Copyright by yuucyf.		2011.07.22
-------------------------------------*/
///
// A 2 by 2 matrix
///
typedef struct tagMatrix2By2
{
      tagMatrix2By2(long long m00 = 0, long long m01 = 0, long long m10 = 0, long long m11 = 0)
      :m_00(m00), m_01(m01), m_10(m10), m_11(m11) 
      {}

      long long m_00;
      long long m_01;
      long long m_10;
      long long m_11;
}S_Matrix2By2;



///
// Multiply two matrices
// Input: sMatrix1 - the first matrix
//        sMatrix2 - the second matrix
//Output: the production of two matrices
///
S_Matrix2By2 MatrixMultiply(const S_Matrix2By2& sMatrix1, const S_Matrix2By2& sMatrix2)
{
      return S_Matrix2By2(
            sMatrix1.m_00 * sMatrix2.m_00 + sMatrix1.m_01 * sMatrix2.m_10,
            sMatrix1.m_00 * sMatrix2.m_01 + sMatrix1.m_01 * sMatrix2.m_11,
            sMatrix1.m_10 * sMatrix2.m_00 + sMatrix1.m_11 * sMatrix2.m_10,
            sMatrix1.m_10 * sMatrix2.m_01 + sMatrix1.m_11 * sMatrix2.m_11);
}

///
// The nth power of matrix 
// 1  1
// 1  0
///
S_Matrix2By2 MatrixPower(int i32N)
{
      assert(i32N > 0);

      S_Matrix2By2 sMatrix;
      if(i32N == 1)
	  {
            sMatrix = S_Matrix2By2(1, 1, 1, 0);
	  }
      else if(i32N % 2 == 0)
      {
            sMatrix = MatrixPower(i32N / 2);
            sMatrix = MatrixMultiply(sMatrix, sMatrix);
      }
      else if(i32N % 2 == 1)
      {
            sMatrix = MatrixPower((i32N - 1) / 2);
            sMatrix = MatrixMultiply(sMatrix, sMatrix);
            sMatrix = MatrixMultiply(sMatrix, S_Matrix2By2(1, 1, 1, 0));
      }

      return sMatrix;
}


long long FibNItemValue_3(int i32NItem)
{
      int aryRes[2] = {0, 1};
      if(i32NItem < 2)
            return aryRes[i32NItem];

      S_Matrix2By2 sPowerNMinus2 = MatrixPower(i32NItem - 1);
      return sPowerNMinus2.m_00;
}



int _tmain(int argc, _TCHAR* argv[])
{
	_tprintf(_T("N item value is %u.\n"), FibNItemValue_3(50));

	return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值