算法-从斐波那契数求解入门动态规划

动态规划在NLP中地位比较尊崇,很多过程譬如词性标注、分词、编辑距离等都会用到它,本文主要通过斐波那契数的求解优化过程来一步步理解动态规划。

一、斐波那契数简介

斐波那契数是数学上的一种数列,它的排列是:1,1,2,3,5,8,13,21,34,55,89,144… 第一项和第二项为1,从第三项开始,每项等于前面两项之和

二、暴力求解

1、分析

  • 已知条件



2、求解分析

从已知条件中惊奇的发现:呀,这不简单吗?初始条件有了,通向公式有了,那我从开始使用递归不就解决了吗?赶紧试一试代码,求解

3、代码实现:

import time
def Fibonacci(n):
    if n < 3:
        return 1
    return Fibonacci(n - 2) + Fibonacci(n - 1)
start=time.time()
result=Fibonacci(30)
end=time.time()
print('结果:{0} 耗时:{1}'.format(result,(end-start)))
#输出
结果:832040 耗时:0.6922831535339355

4、复杂度分析

使用递归,其计算图如下


图片来源参考链接

可以将其看做一颗二叉树,每个节点就是一个计算单元,根据一颗满二叉树节点数与深度的关系 ,时间福复杂度可以近似为,空间复杂度为

三、时间复杂度优化

上面递归的方式在于每次都需要大量重复计算想同的节点,如下图

image.png

那么换一个角度,重复的节点只计算一次,这样时间上是不是就少很多呢,开始编码,自底向上,从一直计算到,并使用数组存储计算过程的结果

import time
import numpy as np
def Fibonacci(n):
    array = np.zeros(n)
    array[0] = 1
    array[1] = 1
    for i in range(2, n):
        array[i] = array[i - 1] + array[i - 2]
    return array[n - 1]
start=time.time()
result=Fibonacci(30)
end=time.time()
print('结果:{0} 耗时:{1}'.format(result,(end-start)))
#输出
结果:832040.0 耗时:0.0

奇迹发生了,时间从上面的0.69变成了0.0,完全是一个飞跃,此时,时间福复杂度为,空间复杂度为。但是还有个问题:是不是忘记了已知条件:

第n个值只和前面的两个值有关,也就是说可以两个内存单元就解决的事情,为啥要存储n个呢?于是进行在优化...

四、空间复杂度优化

  • 目标数只依赖于前面俩个数,再之前的数根本不需要浪费前面等空间
def Fibonacci(n):
  if n<3:
    return 1
  a,b=1,1
  c=0
  for i in range(2,n):
    c=a+b
    a=b
    b=c
  return c

使用三个变量,时间福复杂度为,空间复杂度为,彻底完成了从时间到空间上的优化。

五、动态规划

1、优化总结

从Fibonacci数的计算优化过程中,从数列的初始条件和通项公式,一步步的优化时间和空间复杂度,但是核心都围绕着一个通项公式来计算,这个通向公式就叫做状态转移方程,可以理解为解决问题的关键规律;初始项叫做边界条件,是大问题的最小子问题;动态规划的思路总体而言还是问题的拆分,将,这一步的思考是解决的问题的突破口,叫最优子问题,根据突破口一步一步的拆分,直到拆分到边界条件,这样就完成解决问题的思路,后面就是求解优化的问题。

2、求解

总体而言,动态规划其实就是一种解决问题的思路:
第一步:是找到动态规划的三个核心要素

  • 最优子问题->突破口
  • 状态转移方程->问题规律
  • 边界条件->规律终止条件

第二步:基于时间和空间复杂度进行求解优化->以边界条件开始,基于状态转移方程进行迭代,迭代到真实问题即可

  • 参考

https://mp.weixin.qq.com/s/-bngVVU3O1HaXJDnLYbUgg

你可能感兴趣的:(算法-从斐波那契数求解入门动态规划)