斐波那契数列的--------5种算法(又称“兔子数列”)

本文介绍了斐波那契数列(又称兔子数列)的五种计算方法,包括循环法、递归法、动态规划法、快速矩阵幂以及通项公式。循环法和动态规划法时间复杂度均为O(n),但动态规划法通过滚动数组可优化空间复杂度至O(1)。递归法的时间复杂度为O(2^n),而快速矩阵幂利用矩阵乘法和快速幂算法,能在O(logn)的时间内求解。通项公式通过解特征方程得出,提供了另一种求解思路。
摘要由CSDN通过智能技术生成

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)

一、循环法:
// n 太大会溢出,可以使用数据位更宽的数据类型
func fib(n int) int {
	if n == 1 || n == 2 {
		return 1
	}
	f1 := 1
	f2 := 1
	res := 0  //存放结果 
	for i := 3; i <= n; i++ {
		res = f1 + f2
		f2 = f1
		f1 = res
	}
	return res
}

时间复杂度:O(n)
空间复杂度:O(1)

二、递归法:

根据递推公式:F(n)=F(n - 1)+F(n - 2)可直接得出

func fib(n int) int {
	if n < 2 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

在这里插入图片描述

时间复杂度:因为成二叉树形式 ,所以时间复杂度为O(2^n)
空间复杂度:树约有n-1层,所以空间复杂度为O(n)

三、动态规划法:
  1. 确定 dp数组以及下标的含义:dp[i] 的定义为:第 i 个数的斐波那契数值是 dp[i]
  2. 确定递推公式:题⽬已经把递推公式直接给我们了:状态转移⽅程 dp[i] = dp[i - 1] + dp[i - 2]
  3. 初始化dp数组: dp[0] = 0, dp[1] = 1
  4. 确定遍历顺序: dp[i] 是依赖 dp[i - 1] 和 dp[i - 2] ,所以遍历的顺序是从前到后遍历
func fib(n int) int {
	if n < 2 {
		return n
	}
	dp := make([]int, n+1)
	dp[0] = 0
	dp[1] = 1
	for i := 2; i <= n; i++ {
		dp[i] = dp[i-1] + dp[i-2]
	}
	return dp[n]
}

时间复杂度:O(n)
空间复杂度:O(n)
根据斐波那契数列的性质,我们可以用 滚动数组来优化一下空间复杂度

func fib(n int) int {
	if n < 2 {
		return n
	}
	dp := []int{0, 1}
	result := 0
	for i := 2; i <= n; i++ {
		result = dp[0] + dp[1]
		dp[0] = dp[1]
		dp[1] = result
	}
	return dp[1]
}

时间复杂度:O(n)
空间复杂度:O(1)

方法四:快速矩阵幂

定义一个矩阵:
在这里插入图片描述

可以找到一个矩阵M(这里可以理解为从一个状态转移到另一个状态),使得:

在这里插入图片描述

设:
在这里插入图片描述

所以
在这里插入图片描述

由矩阵乘法可以得到:

af(n)+bf(n-1)=f(n+1)
cf(n)+df(n-1)=f(n)
最终求得:
在这里插入图片描述

由递推公式最终得到:
在这里插入图片描述

因此只要我们能快速计算矩阵 M 的 k 次幂,就可以得到 F(n)的值。
此处需要用到快速幂的算法,不明白的可以看一下快速幂算法_Sakeyuan的博客-CSDN博客:

//矩阵相乘 
func arrayMultiply(a, b [][]int) [][]int {
	c := [][]int{{0, 0}, {0, 0}}
	for i := 0; i < 2; i++ {
		for j := 0; j < 2; j++ {
			c[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j]
		}
	}
	return c
}

// Matrix fast power
func arrayPow(a [][]int, n int) [][]int {   //矩阵快速幂 
	ret := [][]int{{1, 0}, {0, 1}}    //单位矩阵 ,储存结果
	for n > 0 {
		if n&1 == 1 {
			ret = arrayMultiply(ret, a)
		}
		n >>= 1
		a = arrayMultiply(a, a)
	}
	return ret
}

func fib(n int) int {
	if n < 2 {
		return n
	}
	m := [][]int{{1, 1}, {1, 0}}
	result := arrayPow(m, n)
	return result[0][1]   // 由递推公式可以看出f(n)为 result[0][1]位置 
}

时间复杂度:O(logn)
空间复杂度:O(1)

五、通项公式:

根据递推公式 F(n)=F(n - 1)+F(n - 2) 可以得出特征方程:

在这里插入图片描述

求得:
在这里插入图片描述

通解为:
在这里插入图片描述

带入f(0)=0,f(1)=1,解得:
在这里插入图片描述

最终得到通项公式:
在这里插入图片描述

func fib(n int) int {
	sqrt5 := math.Sqrt(5)
	fibN := math.Pow((1+sqrt5)/2, float64(n)) - math.Pow((1-sqrt5)/2, float64(n))
	return int(math.Round(fibN / sqrt5))  /*round()函数四舍五入*/
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kentrl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值