Python 算法之 斐波拉契数列2.0

Python 算法之 斐波拉契数列2.0

文章目录

  • Python 算法之 斐波拉契数列2.0
    • 什么是斐波拉契数列
    • 暴力递归
    • 伪动态规划解法
    • 存储前两个状态
    • 利用Python的特性
    • 为什么标题中有个2.0
    • 相关博客


什么是斐波拉契数列

斐波那契数列1(Fibonacci sequence),又称黄金分割数列、因数学家莱昂纳多·斐波那契(Leonardoda
Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……

Python 算法之 斐波拉契数列2.0_第1张图片


暴力递归

先来看看用递归的方法是怎么写的

def f(n: int) -> int:
    if n == 0:
        return 0
    elif n <= 2:
        return 1
    arr = [0] * (n+1)

    def helper(meno, n):
        if n == 1 or n == 2:
            meno[n] = 1
            return meno[n]
        if arr[n] != 0:             # 优化,在递归之前先去数组查看,如果已经递归过了直接拿出来用
            return arr[n]
        meno[n] = helper(arr, n-1) + helper(arr, n-2)
        return arr[n]
    return helper(arr, n)
  • 这里我做了一个小优化,由于递归时会存在大量重复计算, 如我们要求f(5)=f(4) + f(3),那么一直往下看, f(4) = f(3) + f(n),f(3) = f(2) + f(1),其中f(3) 和 f(2) 计算了两次,这讲属于重复计算,解决这个问题的方法也很简单,在递归前加个判断即可

伪动态规划解法

为什么加上了个伪字,这是因为对于斐波拉契数列来说,并没有涉及到动态规划求最优解(最值)的过程,而是用到了动态规划的解题思维

我们知道,斐波那契数列的原理当前值为前两个值之和,而初始值为0和1

那么,问题的 状态转移方程 就出来了,即描述问题结构的数学公式

Python 算法之 斐波拉契数列2.0_第2张图片

def fib(n: int) -> int:
    if n == 0:
        return 0
    elif n <= 2:
        return 1
    n = n+1                             # 再计算斐波那契数列时存在初始值0,得对n进行+1操作
    dp = [0] * n                        # 定义DP Table
    for i in range(n):
        if i in (0, 1):                 # 对前两个值初始化
            dp[i] = i
        else:
            dp[i] = dp[i-1] + dp[i-2]   # 上一个值与上两个值之和即为下一个值
    return dp[-1]

将上面的代码进行简化,可以这么去写

def fib(n: int) -> int:
    n += 1								# 再计算斐波那契数列时存在初始值0,得对n进行+1操作
    dp = [0] * n
    dp[:2] = [0, 1]						# 对前两个值初始化
    for i in range(2, n):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n - 1]

存储前两个状态

此方法是根据上面所使用到的伪动态规划方法优化而来,根据 斐波那契数列 转移方程可知,当前值与前两个值有关,那么只要想办法存储前两个值就可以进一步优化

def fib(n: int) -> int:
    if n == 0:
        return 0
    prev, curr = 0, 1			# 定义初始值
    for _ in range(n-1):
        sum_ = prev + curr      # 计算前两个值之和
        prev = curr             # 当前的值变成上一个值
        curr = sum_             # 更新当前值
    return curr                 # 返回当前值
  • 这个技巧美名其曰 状态压缩,从最初空间复杂度:O(n) 降为 O(1)
  • 存储前两个状态 的解法 与上面的 伪动态规划解法 进行对比,相信更加明白为什么要怎么做

利用Python的特性

在python中,可以实现同时赋值,其写法如下

a, b = 1, 2

根据这个特性,再去改写一下代码

def fib(n: int) -> int:
    if n == 0:
        return 0
    prev, curr = 0, 1
    for _ in range(n-1):
        prev, curr = curr, prev+curr    # 同时进行赋值
    return curr                         # 返回当前值

在Python中,我们成功从最初的 伪动态规划 需要新建一维数组的操作,优化到上面的代码

如果即想 根据存储前两个状态解析,又想存储斐波那契数列 该怎么做?

很简单,新建一个一维数组,并且在获得当前值时存储进去即可

def fib(n: int) -> int:
    if n == 0:
        return 0
    prev, curr = 0, 1
    f_arr = [prev, curr]                # 新建数组,并且将初始值放入
    for _ in range(n-1):
        prev, curr = curr, prev+curr    # 同时进行赋值
        f_arr.append(curr)              # 添加新的当前值
    return curr                         # 返回当前值

为什么标题中有个2.0

在第一次编写的时候(大概是大半年前),我还是个算法小白,在学习途中一直模仿着别人的解法写下笔记,如今回过头来,我逐渐明白为什么要去怎么做,看着第一次写下的笔记发现有着许多不足,于是我第二次修改编写,在标题上加个2.0


相关博客

  • Python 如何求素数、质数
  • Python 算法之 设计哈希集合

  1. 斐波纳契数列 维基百科 ↩︎

你可能感兴趣的:(Python,算法,python,斐波纳契数列,斐波纳契)