用变长数组输出fibonacci数列前n项_斐波那契数列的N种解法

cfa85c3d45d7803fc8181e1ffc6a9005.png

近日在极客时间上看到winter老师讲解斐波那契数列的解法,跟着把代码实现了一遍,分享在这里

斐波那契数列是指每一项均为前两项之和的数列,标准的斐波那契数列为0,1,1,2,3,5,8,13,21,34,55,89,144,233 ...

斐波那契数列问题有多种表述形式(马甲),常见的有以下几种

  • 兔子繁殖问题:农场里起初有一对兔子,每年繁殖一对小兔,小兔一年后成年,问n年后农场里一共有多少对兔子?
  • 跳台阶问题:小和尚从山脚下往山上跳台阶,每次可以跳一级或者两级台阶,问要跳到n级台阶,一共有多少种跳法?
  • 矩形覆盖问题:用n个2*1的小矩形无重叠地完全覆盖一个2*n的大矩形,总共有多少种方法?

下面就来由简入深,一一讲解每种解法的实现及复杂度

解法一:递归实现

/**

这种方式代码看起来很简洁优美,但实际上却是效率最低,复杂度最高的方法,包含了许多的重复计算,代码复杂度是O(2^n)


理论上计算斐波那契数列第n项只需要n次运算即可得到,于是我们有了下面的第二种解法

解法二:迭代实现

/**

到这里,我们去除了不必要的重复计算,将代码复杂度减少到了O(n)


单纯从代码层面似乎没有太多可以优化的空间了,接下来让我们转变思维,从数学角度来思考一下

解法三:通项公式实现

其实前人已经为我们铺好了路,斐波那契数列已经被证实存在以下通项公式(具体推导过程还是挺复杂的,在这里不做介绍了,有兴趣的同学自己上网查阅相关资料)

/**

这里用到的Math.pow(),求N次幂的方法,可以通过计算平方来逼近N次幂,因此复杂度是O(log(n))

Math.pow()方法在JDK中是native方法实现的,我们可以自己来实现一版,原理就是将n转化为二进制,对于为1的位数,乘上x对应的次方

/**

注意,受限于浮点数的精度问题,pow的结果需要做一个Math.round()操作使其变为整数


以上的通项公式虽然降低了算法复杂度,但同时引入了浮点数这种相对耗性能的运算,另外还需要处理精度的问题,有没有其他的解法能达到同样的复杂度而不需要用到浮点数呢?于是我们把目光投向了线性代数的知识

解法四:矩阵实现

回到第二步的迭代实现,其实每一次迭代都是在做将a,b转化为b,a+b的计算,如果我们将其转化为矩阵,就有了以下公式

将斐波那契数列代入这个公式,其最初的两项为a=0,b=1,我们可以得到

所以我们先定义一个2*2矩阵的乘法函数

/**

再用该矩阵乘法来实现矩阵n次幂函数

/**

最后我们就可以实现矩阵版本的迭代计算了,复杂度同样为O(log(n))

/**

以上就是斐波那契数列常用的解法,后两种代码复杂度同为O(log(n)),矩阵解法在代码实现上要麻烦些,可读性略低,具体使用看各位的喜好

最后的思考题:

由于斐波那契数列的值会很大,n达到90左右long类型就会溢出,要计算大值只能引入BigInteger,BigDecimal这些相对耗性能的类型,那么矩阵实现的方式可能会因为运算较多而性能有所损耗,有兴趣的同学可以实测一把

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值