动态规划基础知识

简介: 简介: 公众号:程序员学长,分享编程知识,这篇文章讲解了《动态规划基础知识》,如何喜欢,记得点个关注吧

动态规划基础知识

问题模型

动态规划一般是用来解决最优解问题,而在解决的过程中是需要经历多个阶段的决策,每个阶段都会对应一组状态。我们需要找到一组决策,经过这些决策后,能求出问题的最优解。我们把这类问题抽象成“多阶段决策最优模型”。
动态规划具有三大特征,分别是最优子结构、无后效性和重复子问题,只有当原问题满足这三大特征时,我们才能使用动态规划的算法思想来解决。下面我们来分别看一下这三个特征。

  • 最优子结构
    最优子结构规定了原问题和子问题的关系。原问题的最优解包含子问题的最优解。反过来说,我们可以通过子问题的最优解来求出原问题的最优解。
  • 无后效性
    (1)无后效性是指在推导后面阶段状态的时候,只依赖于前面阶段的的状态,而不会去关心这个状态是怎么一步步得来的。比如斐波那契数列 F(5)=F(4)+F(3),则可以看出 F(5) 只依赖于 F(4) 和 F(3) 这两个状态的值,而不用管他们是如何得来的。
    (2)一旦某个状态确定了,就不受之后阶段的决策影响。
  • 重复子问题
    就是原问题经过拆分成多个子问题时,子问题和子问题之间存在重复计算的情况。

下面我们结合实例,来比较透彻的理解一下上面的理论部分。

问题描述:

我们假设有一个 n*n 的矩阵 w[n] [n](矩阵中存储正整数)。棋子从矩阵的左上角开始移动到矩阵的右下角。棋子每次只能向下或者向右移动一步。棋子可以有很多不同的路径走完这个矩阵。我们把每条路径经过的数字相加作为路径长度,那最短的路径长度是多少呢?
image-20211222183754767
我们从 start 开始走,一直走到 end 的位置,一共需要走 2 (n-1) 步,也就对应着 2 (n-1) 个阶段,每个阶段都有向下走和向右走两种决策,不同的决策对应着不同的状态。所以这符合多阶段决策,而最后求解的是最短路径长度,所以符合动态规划的问题模型。
接下来我们看它是否符合动态规划的三个特征呢?如下所示,从(0,0)位置走到(1,1)位置有两种走法,所以符合重复子问题。
image-20211222184015066
下面我们再来看一下无后效性这个特征。我们要想走到(i,j)这个位置,我们只能通过(i-1,j)和(i,j-1)这两个位置移动过来,也就是说,我们只需要关心(i-1,j)和(i,j-1)这两个位置的状态,而不必关系是如何从(0,0)位置走到这个位置的。而且,我们这里只允许向下或者向右走,不允许后退,所以前面阶段的状态确定之后,就不会被后面阶段的决策所改变。所以这个问题是符合“无后效性”这个特征的。
我们把从(0,0)位置走到(i,j)位置的最短路径记为为 min(i,j),因为我们只能向下或者向右移动,所以我们只能从(i-1,j)和(i,j-1)两个位置到达(i,j)。换句话说,就是 min(i,j) 只能通过 min(i-1,j) 和 min(i,j-1) 两个状态推导出来。所以这个问题就符合“最优子结构”。

min(i,j) = w[i][j] + min(min(i-1,j), min(i,j-1))

所以这个问题是符合动态规划问题模型的,所以我们可以采用动态规划思想来解决这个问题。

求解思路

解决动态规划问题,一般有状态转移表法和状态转移方程法这两种方法。

  1. 状态转移表法

我们先定义一个状态表,一般状态表都是二维的。我们根据决策的先后顺序,从前往后,根据递归关系,分阶段的填充状态表中的每个状态。最后,我们把递归填表的过程翻译成代码就完成了。
接下来我们来看一下如何用状态转移表法来求最短路径这个问题的。
首先创建一个二维数组,表中的行、列表示棋子所在的位置,表中的数值表示从起点到这个位置的最短路径。然后,我们按照决策过程依次去填表,前两步的走法如下图所示:
image-20211222184600442
代码实现如下所示:

def minDis(data,n):
    status=[[0 for _ in range(n)] for _ in range(n)]
    sum=0
    #第一行赋值
    for i in range(n):
         sum=sum+data[0][i]
         status[0][i]=sum
    #第一列赋值
    sum=0
    for i in range(n):
         sum=sum+data[i][0]
         status[i][0]=sum
    for i in range(1,n):
         for j in range(1,n):
              status[i][j]=data[i][j]+min(status[i-1][j],status[i][j-1])
    return status[n-1][n-1]
data=[[1,3,5,7,2],
      [3,6,5,2,1],
      [7,4,1,6,5],
      [1,3,8,2,3],
      [4,3,1,6,4]]
n=5
print(minDis(data,n))
  1. 状态转移方程法

状态转移方程法和递归的解题思路类似。我们根据最优子结构,写出递推公式,也就是所谓的状态转移方程。然后根据状态转移方程,实现代码就好了。这里一般有两种实现方法,一种是递归加备忘录方法,另一种是迭代递推。
对于最短路径这个例子,它的状态转移方程如下所示:

min(i,j) = w[i][j] + min(min(i-1,j), min(i,j-1))

我们这里采用递归加备忘录的方法来实现,另一种迭代递推的实现和状态转移表法的代码实现是一致的,只是思路不同而已。

import sys
data=[[1,3,5,7,2],
      [3,6,5,2,1],
      [7,4,1,6,5],
      [1,3,8,2,3],
      [4,3,1,6,4]]
n=5
mem=[[0 for _ in range(5)] for _ in range(5)]
def minDis(i,j):
    if i==0 and j==0:
        return data[0][0]
    if mem[i][j]>0:
        return mem[i][j]
    minLeft = sys.maxsize
    if j-1>=0:
        minLeft=minDis(i,j-1)
    minUp = sys.maxsize
    if i-1>=0:
        minUp=minDis(i-1,j)
    current=data[i][j]+min(minLeft,minUp)
    mem[i][j]=current
    return current
print(minDis(n-1,n-1))
相关文章
|
资源调度 JavaScript
vue3 vant上传图片
vue3 vant上传图片
657 0
|
算法 程序员 Go
[软件工程导论(第六版)]第6章 详细设计(复习笔记)
[软件工程导论(第六版)]第6章 详细设计(复习笔记)
|
监控 安全 中间件
Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
这篇文章介绍了什么是Next.js中的中间件以及其应用场景。中间件可以用于处理每个传入请求,比如实现日志记录、身份验证、重定向、CORS配置等功能。文章还提供了一个身份验证中间件的示例代码,以及如何使用限流中间件来限制同一IP地址的请求次数。中间件相当于一个构建模块,能够简化HTTP请求的预处理和后处理,提高代码的可维护性,有助于创建快速、安全和用户友好的Web体验。
204 0
Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
|
6月前
|
SQL 关系型数据库 MySQL
【MySQL】SQL分析的几种方法
以上就是SQL分析的几种方法。需要注意的是,这些方法并不是孤立的,而是相互关联的。在实际的SQL分析中,我们通常需要结合使用这些方法,才能找出最佳的优化策略。同时,SQL分析也需要对数据库管理系统,数据,业务需求有深入的理解,这需要时间和经验的积累。
177 12
|
11月前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
8月前
|
UED
如何使用网站模版制作网站。
使用模板快速搭建网站,省时省力。选择模板、自定义编辑、发布上线。注意模板匹配、内容原创、遵守平台法规。
104 7
|
12月前
|
存储 前端开发 JavaScript
前端技术深度探索:从基础到现代框架的实践之旅
前端技术深度探索:从基础到现代框架的实践之旅
212 3
|
运维 Linux Apache
【一键变身超人!】Puppet 自动化运维神器 —— 让你的服务器听话如婴儿,轻松管理资源不是梦!
【8月更文挑战第9天】随着云计算与容器化技术的发展,自动化运维已成为现代IT基础设施的核心部分。Puppet是一款强大的自动化工具,用于配置管理,确保系统保持预期状态。通过易于理解的配置文件定义资源及其依赖关系,Puppet实现了“基础设施即代码”的理念。本文简要介绍了Puppet的安装配置方法及示例,包括Puppet Agent与Master的安装、基本配置步骤和一个简单的Apache HTTP Server管理示例,展示了Puppet在实际应用中的强大功能与灵活性。
203 9
|
缓存 安全 Java
Spring高手之路21——深入剖析Spring AOP代理对象的创建
本文详细介绍了Spring AOP代理对象的创建过程,分为三个核心步骤:判断是否增强、匹配增强器和创建代理对象。通过源码分析和时序图展示,深入剖析了Spring AOP的工作原理,帮助读者全面理解Spring AOP代理对象的生成机制及其实现细节。
338 0
Spring高手之路21——深入剖析Spring AOP代理对象的创建
|
前端开发
CSS:去除input和button边框以及选中时边框默认样式
CSS:去除input和button边框以及选中时边框默认样式