动态规划(用空间换时间的算法)原理逻辑代码超详细!参考自《算法导论》

news2024/12/23 8:11:26

动态规划(用空间换时间的算法)-实例说明和用法详解

  • 动态规划(DP)思想
  • 实例说明
    • 钢条切割问题
    • 矩阵链乘法问题
  • 应用满足的条件和场景

本篇博客以《算法导论》第15章动态规划算法为本背景,大量引用书中内容和实例,并根据书中伪代码给出python代码复现,详解算法的核心逻辑和实现过程。

动态规划(DP)思想

动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为重叠的子问题进行解决,从而一步步获取最优解的处理算法。

动态规划与分治方法相似,都是通过组合子问题的解来求解原问题(在这里“programming”指的是一种表格法,并非编写计算机序)。但是分治方法将问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,求出原问题的解。与之相反,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。

在这种情况下,分治算法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。

实例说明

动态规划方法常用来求解最优化问题,下面分别给出二个例子来说明:第一个是将钢条割成短钢条,使得总价值最高;第二个是解决如何用最少的标量乘法操作完成一个矩阵链相乘的运算

钢条切割问题

下面是钢条切割问题的背景:

问题背景说明
比如,下图给出长度为4英寸钢条所有可能的切割方案:
切割方案
其中,钢条上方的数字为每段钢条对应的价格。不同的切割方案对应不同的价格,我们发现,将一段长度为4英寸的钢条切为两段各长2英寸的钢条,将产生 P 2 P_2 P2+ P 2 P_2 P2=5+5=10的收益,为最优解,即对应上图中的方案C。

下面我们逐步分析出钢条切割问题的最优收益的递推公式:
在这里插入图片描述
更为一般的收益公式,长度为n的钢条切割后的最大收益 r n r_n rn表示如下:
在这里插入图片描述
一种更简单的递归方法:
在这里插入图片描述
其中,(15.2)式中的 p i p_i pi指长度为i的钢条对应的价格,直接查表得到,不用再进行切割。

如果用传统的递归方法求解,一旦n的规模稍微变大,程序运行的时间会变得相当长,因为这种方法反复用相同的参数值对自身进行递归调用,即它反复求解相同的子问题。时间呈指数级增长。下图给出n=4时,递归树的调用过程:
在这里插入图片描述
动态规划方法的运行时间是多项式阶的。
在这里插入图片描述
动态规划有两种等价的实现方法:
在这里插入图片描述
下面给出自底向上法的代码实现过程:
在这里插入图片描述
在上面的伪代码中,输入为两个变量,价格表数组和长度n;输出长度为n的钢条得到的最优收益。

下面计算长度为9时,钢条切割的最大收益:

import numpy as np

def Bottom_Up_Cut_Rod(p, n):
    r = []
    r.insert(0, 0)
    for j in range(1, n + 1):
        q = float('-inf')
        for i in range(1, j + 1):
            q = max(q, p[i - 1] + r[j - i])
        r.insert(j, q)
    return r[n]

if __name__ == '__main__':
    # 出售长度为i(i=1,2,3,,,10)的钢条所对应的价格样表
    p = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
    n = 9
    result = Bottom_Up_Cut_Rod(p, n)
    print("长度为{}时,".format(n))
    print("对应的最优收益值为{}。".format(result))

在这里插入图片描述
下图是长度为1、2、3…,对应的最优切割方案和收益。
在这里插入图片描述

思想:为了求解规模为 n 的原问题,我们先求解形式完全一样,但规模更小的子问题。即当完成首次切割后,我们将两段钢条看成两个独立的钢条切割问题实例。我们通过组合两个相关子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大者,构成原问题的最优解。

我们称钢条切割问题满足最优子结构性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。

矩阵链乘法问题

矩阵链乘法问题即是多个矩阵相乘,找出最快计算次序的问题。

在这里插入图片描述
在这里插入图片描述

对矩阵链加括号的方式会对乘积运算的代价产生巨大影响,下面举例来说明:

在这里插入图片描述
因此,如果A是pXq的矩阵,B是qXr的矩阵,那么乘积 C是pXr的矩阵。计算 C所需时间由标量乘法的次数决定,即 pqr。下面我们将用标量乘法的次数来表示计算代价

在这里插入图片描述

传统方法:穷举所有可能的括号化方案不会是一个高效的算法,因为它的运行时间仍然是指数级增长的。

下面定义矩阵链问题的计算代价公式,令m[i,j]表示计算矩阵 A i , . . . , j A_{i,...,j} Ai,...,j所需标量乘法次数的最小值。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
假设最优分割点k是未知的,则递归求解公式变为:
在这里插入图片描述
下面用一个例子来说明:
在这里插入图片描述
上图是我手写的一个例子,为了帮助理解。其中, A 1 A_1 A1:2 × \times × 3 ,表示 A 1 A_1 A1的维度为2行3列,P列表存放的是四个矩阵的维度,其中前一个矩阵的列数等于后一个矩阵的行数。要求出 A 1 A_1 A1 A 2 A_2 A2 A 3 A_3 A3 A 4 A_4 A4的计算代价,假设分割点k=2,则计算顺序为 ( ( A 1 A 2 ) ( A 3 A 4 ) ) ((A_1A_2)(A_3A_4)) ((A1A2)(A3A4))。其中, ( A 1 A 2 ) (A_1A_2) (A1A2)的计算代价对应上图中的矩阵C, ( A 3 A 4 ) (A_3A_4) (A3A4)的计算代价对应上图中的矩阵D,最后再令C与D相乘,算的最后的计算代价(总次数)为48。

下面给出的过程 MATRIX-CHAIN-ORDER实现了自底向上表格法:
各输入变量的含义如下:
在这里插入图片描述
在这里插入图片描述
输出变量为最优代价矩阵m和最优分割点矩阵k。

下面给出代码的计算过程帮助理解。假设n=4, p = [ p 0 , p 1 , p 2 , p 3 , p 4 ] p = [p_0,p_1,p_2,p_3,p_4] p=[p0,p1,p2,p3,p4],求 A 1 A 2 A 3 A 4 A_1A_2A_3A_4 A1A2A3A4的最优代价,即求m[1,4]。计算过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

左边是最优代价矩阵,右边是最优分割点矩阵

以图15-5的数据为例,python代码如下:

import numpy as np

def MATRIX_CHAIN_ORDER(p):
    n = len(p) - 1
    # 定义计算代价矩阵m
    m = np.zeros((n, n))
    # 定义最优分割点矩阵s
    s = np.zeros((n, n))
    for l in range(2, n + 1):
        for i in range(1, n - l + 2):
            j = i + l - 1
            m[i - 1, j - 1] = float('inf')
            for k in range(i, j):
                q = m[i - 1, k - 1] + m[k, j - 1] + p[i - 1] * p[k] * p[j]
                if q < m[i - 1, j - 1]:
                    m[i - 1, j - 1] = q
                    s[i - 1, j - 1] = k
    print(m)  # 每个元素对应长度为j-i+1的矩阵所需要最小计算代价
    print(s)  # 对应长度为j-i+1的矩阵最优分割点
    return m, s

if __name__ == '__main__':
    p = [30, 35, 15, 5, 10, 20, 25]
    MATRIX_CHAIN_ORDER(p)

运行结果如下:

在这里插入图片描述
能够看出,跑出的结果与书上的两个矩阵完全相同。(只不过书中对矩阵沿主对角线方向进行了旋转)

思路:一个非平凡的矩阵链乘法问题实例的任何解都需要划分链,而任何最优解都是由子问题实例的最优解构成的。因此,为了构造一个矩阵链乘法问题实例的最优解,我们可以将问题划分为两个子问题( A i A i + 1 ⋅ ⋅ ⋅ A k A_iA_{i+1}···A_k AiAi+1⋅⋅⋅Ak A k + 1 ⋅ ⋅ ⋅ A j A_{k+1}···A_j Ak+1⋅⋅⋅Aj)子题实例的最优解,然后将子问题的最优解组合起来。我们必须保证在确定分割点时,已经考察了所有可能的划分点,这样就可以保证不会遗漏最优解。

应用满足的条件和场景

应用动态规划方法求解的最优化问题应该具备的两个要素:最优子结构子问题重叠

在这里插入图片描述
在这里插入图片描述

动态规划算法可以用来求解最优化问题,除了本文给出的两个例子外,常用的场景包括求解斐波那契数列,求解最长公共子序列、01背包问题和最优二叉搜索树等。想要深入了解该算法的原理和应用场景,具体可以阅读《算法导论》,我有电子版的,需要的兄弟姐妹可以私信我取。

综上所述,满足最优子结构和子问题重叠性质的问题可以利用动态规划思想建模,这种方法会开辟内存,存储以往的运算结果,再下次遇到时直接调用而不再重复计算,因此大大节省了时间,是一种经典的用空间换时间的算法,而大多数情况下,项目对时间的要求往往更严格,相比对内存的占用情况就宽松很多了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/847253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【枚举,构造】CF1582 C D

Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a; 思路很简单&#xff0c;只删除一种&#xff0c;直接枚举删除的是哪一种即可 但是回文子序列的判定我vp的时候写的很答辩&#xff0c;也不知道为什么当时要从中间往两边扫&#xff0c;纯纯自找麻烦 然后就越改越…

题解:散列查找(拉链法)出现冲突时,在散列表冲突点向外延伸一条链表(单链表),怎么使用memset函数

一、链接 840. 模拟散列表 二、题目 维护一个集合&#xff0c;支持如下几种操作&#xff1a; I x&#xff0c;插入一个数 xx&#xff1b;Q x&#xff0c;询问数 xx 是否在集合中出现过&#xff1b; 现在要进行 NN 次操作&#xff0c;对于每个询问操作输出对应的结果。 输…

SAP 开发编辑界面-关闭助手

打开关闭助手时的开发界面如下&#xff1a; 关闭关闭助手后的界面如下&#xff1a; 菜单栏&#xff1a; 编辑--》修改操作--》关闭助手

VLC视频直播低时延配置

默认的VLC的播放时延是比较高的&#xff0c;一般是秒级别&#xff0c;默认配置的话&#xff0c;都是5秒左右&#xff0c;这种默认配置是为了利用缓存机制&#xff0c;使播放体验更加流畅&#xff0c;对于需要更低时延的播放测试的话&#xff0c;并不适合&#xff0c;需要调整一…

商城-学习整理-基础-商品服务API-属性分组(七)

目录 一、创建系统菜单二、开发属性分组1、将三级分类功能抽取出来2、编写后端代码3、属性分组新增功能4、属性分组修改回显功能 三、品牌管理1、分页显示有点问题&#xff0c;使用MyBatis-Plus有点问题&#xff0c;需要使用分页插件&#xff0c;给容器中放一个2、修改模糊查询…

Netty: 向ChannelPipeline中添加ChannelHandler的顺序

Netty中的ChannelHandler有inbound handler&#xff0c;处理接收数据的过程&#xff1b;有outbound handler&#xff0c;处理发数据的过程。当然&#xff0c;也有的handler既处理接收的数据 &#xff0c;也处理发送的数据。 每个channel对应一个ChannelPipeline。handler被添加…

多语言多用户跨境电商系统搭建--独立站源码制作

开发一个多语言多用户跨境电商系统搭建需要考虑以下几个方面&#xff1a; 1. 系统架构设计&#xff1a;选择一个适合多语言多用户跨境电商系统的开源框架或者自行设计系统架构。确保系统的稳定性和扩展性。 2. 多语言支持&#xff1a;设计一个多语言支持功能&#xff0c;使用…

电子邮件数据加密的工作原理

电子邮件数据加密是通过使用密码学算法对电子邮件的内容进行转换&#xff0c;使得只有授权的接收方能够解读邮件内容。下面是电子邮件数据加密的一般工作原理&#xff1a; 密钥生成&#xff1a;发送方和接收方分别生成自己的密钥对。密钥对通常包括公钥和私钥。公钥用于加密和验…

phonopy中频率单位的换算

phonopy给出的单位是THz&#xff0c;有时会向cm-1和eV单位进行转换。在phonopy中进行单位转换时&#xff0c; 主要是对在phonopy中使用的参数factor进行修改&#xff0c;我们平时声子谱导出使用命令是&#xff1a; phonopy band.conf &#xff08;导出的是默认单位THz&…

Redisson 3.23.1 正式发布,官方推荐的 Redis 客户端

导读Redisson 3.23.1 现已发布&#xff0c;这是一个 Java 编写的 Redis 客户端&#xff0c;具备驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;功能&#xff0c;并获得了 Redis 的官方推荐。 此版本更新内容如下&#xff1a; Improvement 减少了 RLiveObjectSer…

别再被问倒了!Mysql索引竟然在这些情况下失灵?

嗨&#xff0c;亲爱的读者们&#xff01;小米又来啦~ 今天我们要聊一个在数据库面试中常常被问到的热门话题&#xff1a;Mysql索引失效。想要在面试中脱颖而出&#xff0c;掌握这个知识点可是必不可少哦&#xff01;废话不多说&#xff0c;咱们现在就深入剖析一下&#xff0c;看…

SQL Server 查询数据并汇总相关技巧 23.08.08

GROUPING 是一个聚合函数,它产生一个附加的列&#xff0c;当用 CUBE 或 ROLLUP 运算符添加行时&#xff0c;附加的列输出值为1&#xff0c;当所添加的行不是由 CUBE 或 ROLLUP 产生时&#xff0c;附加列值为0。 仅在与包含 CUBE 或 ROLLUP 运算符的 GROUP BY 子句相联系的选择…

【模拟 + 离线】CF1719 C

Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a; 手摸以下样例容易发现 对于最大值左边的数&#xff0c;答案可以直接模拟轮数得到 对于最大值&#xff0c;答案直接就是pos 对于最大值右边的数&#xff0c;答案就是0 接下来就是如何模拟轮数的问题了 一开始…

直流浪涌保护器与交流浪涌保护器的区别和作用

在现代社会&#xff0c;电力作为生产、生活的重要能源&#xff0c;其稳定供应显得尤为重要。然而&#xff0c;电力系统常常受到电压浪涌的干扰&#xff0c;这种突发的电压冲击可能给设备带来损害。为了应对这一问题&#xff0c;直流浪涌保护器和交流浪涌保护器应运而生&#xf…

Android车机录制视频报错,竟是编码器的锅 ?

1. 现象描述 工作中有一个项目&#xff0c;使用的是CameraView这个第三方相机库来调用相机。 CameraView封装了Camera1和Camera2&#xff0c;内部做了很多功能的封装&#xff0c;API使用起来相对比较简单。 在App中接入后&#xff0c;手机上能够正常录制视频&#xff0c;看上去…

Deep Networks with Stochastic Depth - 动态随机网络

文章目录 基本结构ResNet的公式改造效果计算前向传播过程 实验结果CIFAR数据集结果SVHN数据集结果训练时间的比对极深网络的对比测试ImageNet的测试结果测试过程中的结果 网络结构的Hyper-parameter比对测试 前面两篇是讲经典网络ResNet的&#xff1a; ResNet1 ResNet2 这个残…

中介者模式(Mediator)

中介者模式是一种行为设计模式&#xff0c;可以减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互&#xff0c;迫使它们通过一个封装了对象间交互行为的中介者对象来进行合作&#xff0c;从而使对象间耦合松散&#xff0c;并可独立地改变它们之间的交互。中介者…

【设计模式——学习笔记】23种设计模式——中介者模式Observer(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入案例一普通实现中介者模式 案例二 介绍基础介绍登场角色尚硅谷 《图解设计模式》 案例实现案例一&#xff1a;智能家庭类图实现 案例二&#xff1a;登录页面逻辑实现说明类图实现 总结文章说明 案例引入 案例一 普通实现 在租房过程中&#xff0c;客户可能…

回看雷军演讲,我对项目经理人的发展又有了2点想法……

大家好&#xff0c;我是老原。 最近在回看一些大佬演讲&#xff0c;看到雷军的演讲时&#xff0c;说实话我蛮激动的&#xff0c;最近常有粉丝朋友找我闲聊&#xff0c;也工作十几年&#xff0c;不知道怎么才能寻求突破&#xff1f; 而小米本身&#xff0c;不就是一直在突破吗…

C高级-day4

#!/bin/bash function fun1(){arr[0]id -u $1arr[1]id -g $1echo ${arr[*]} }arr(fun1 ubuntu) echo ${arr[*]}冒泡排序 void Maopao(int arr[],int len){for(int i1;i<len;i){int count0;for(int j0;j<len-i;j){if(arr[j]>arr[j1]){int tarr[j];arr[j]arr[j1];arr[j…