《算法技术手册》一3.6.3 动态规划

简介: 本节书摘来华章计算机《算法技术手册》一书中的第3章 ,第3.6.3节, George T.Heineman Gary Pollice Stanley Selkow 著 杨晨 曹如进 译 译更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.6.3 动态规划

动态规划是分治算法的一个衍生,它将一个问题切分成多个更加简单的子问题,然后按照顺序逐个解决。对于每个子问题,它只求解一次,然后将结果存储起来以供后续使用,这样可以避免重复计算。此外,动态规划在计算当前解时,会结合较小规模的子问题的解,并且不断增加子问题的规模。很多情况下,最终计算出来的解即为全局最优解。
动态规划常用于求解最值优化类问题上。下面通过一个示例来阐释动态规划算法。
科学家经常通过比对DNA序列来确定其相似性。现有这样一个DNA序列——由A、C、T、G所组成。因此,一个DNA序列就可以使用一个字符串进行表示,而DNA序列的相似性就可以转化成计算两个字符串的最小编辑距离。也就是说,给定一个字符串s1和一个目标字符串s2,需要使用最少的编辑操作来将s1转化成s2。支持的编辑操作包括:
2017_09_20_125647
例如字符串s1为“GCTAC”,可以通过3次编辑操作来得到目标字符串“CTCA”:
2017_09_20_125717
当然,还有其他方式来得到目标字符串,但是最少也需要3次编辑操作。对于初学者来说,计算出需要操作的次数就已经足够了,我们暂时还不需要计算出具体的操作顺序。
动态规划的关键点是存储子问题的结果。在这个例子中,我们可以使用一个二维矩阵mi,来记录s1的前i个字符和s2的前j个字符之间的最小编辑距离。这个矩阵可以初始化为:
2017_09_20_125811
在这个矩阵中,行标为i,列标为j。以m0为例,这个元素表示的是s1的前0个字符(也就是空字符串)和s2的前4个字符串(整个字符串“CTCA”)之间的最小编辑距离。可以很容易计算出来,m0的值为4,因为我们需要插入全部s2的4个字符到s1中去。同样的,我们也可以轻易得到m3的值为3,因为从s1的前3个字符来考虑,我们需要删除这所有3个字符来得到s2的前0个字符(即空字符串)。
动态规划的巧妙之处在于如何不断地从规模较小的子问题的解中构建出当前的最优解。让我们先来看看m1,这是表示s1的第一个字符G和s2的第一个字符C之间的编辑距离,现在有3个选择:
2017_09_20_125855
我们肯定会选择操作数最小的,所以m1=1。那么怎么推广这个选择策略呢?试考虑图3-2所示的计算过程。
每一次计算mi时都面临3个选择:
2017_09_20_125956
现在我们脑海里面就应该有一幅图片,生动地描述了动态规划是如何一步一步地按照既定的顺序从小规模的子问题开始逐渐处理的(即,从第一行到最后一行,每行按照最左到最右的顺序访问,如例3-3所示)。计算过程是按照行标i从1到len(s1)逐渐进行。当矩阵m初始化之后,我们利用嵌套的for循环计算每个子问题的最优解,直到m中的所有值都已经计算完毕。这个过程通常不用递归,而是会将之前计算过的结果存储起来。最终的最优解即为mlen(s1)的值。
2017_09_20_130049
图3-2:计算mi
例3-3:使用动态规划计算最小编辑距离(Python)
def minEditDistance(s1, s2):
"""计算将s1转换到s2的最小编辑距离"""
len1 = len(s1)
len2 = len(s2)

2017_09_20_130155创建一个二维结构
2017_09_20_130155 满足mi = 0, 0≤i≤len1, 0≤j≤len2
m = [None] * (len1 + 1)
for i in range(len1 + 1):

m[i] = [0] * (len2 + 1)

2017_09_20_130155横向、纵向设置初始化操作次数
for i in range(1, len1 + 1):

m[i][0] = i

for j in range(1, len2 + 1):

m[0][j] = j

2017_09_20_130155 计算最优解
for i in range(1, len1 + 1):

for j in range(1, len2 + 1):
  cost = 1
if s1[i - 1] == s2[j - 1]: cost = 0
replaceCost = m[i - 1][j - 1] + cost
removeCost = m[i - 1][j] + 1
insertCost = m[i][j - 1] + 1
m[i][j] = min(replaceCost, removeCost, insertCost)

return mlen1
表3-5给出了矩阵m的最终值。
表3-5:所有子问题的解
2017_09_20_130329
例如m3=1,因为字符串“GCT”和“CT”的最小编辑距离只有1,仅仅需要删掉GCT的第一个字符即可。这段代码仅仅计算了最小编辑距离,如果需要记录操作顺序,那么就需要另外一个矩阵prev来存储。previ记录了在计算mi时所选择的3个操作中的哪一个。要恢复操作,我们可以从mlen(s1)开始,使用previ的值,一步一步地向后追溯,直到m0为止。修改过的代码见例3-4。
例3-4:使用动态规划计算最小编辑距离,并且记录操作顺序
REPLACE = 0
REMOVE = 1
INSERT = 2
def minEditDistance(s1, s2):
"""计算将s1转换到s2的最小编辑距离,并且记录操作顺序 """
len1 = len(s1)
len2 = len(s2)

2017_09_20_130155创建一个二维结构
2017_09_20_130155满足mi = 0, 0≤i≤len1, 0≤j≤len2
m = [None] * (len1 + 1)
op = [None] * (len1 + 1)
for i in range(len1 + 1):

  m[i] = [0] * (len2 + 1)
  op[i] = [-1] * (len2 + 1)

2017_09_20_130155横向、纵向设置初始化代价
for j in range(1, len2 + 1):

  m[0][j] = j

for i in range(1, len1 + 1):

  m[i][0] = i

2017_09_20_130155 计算最优解
for i in range(1, len1 + 1):

for j in range(1, len2 + 1):
  cost = 1
  if s1[i - 1] == s2[j - 1]: cost = 0
  replaceCost   = m[i - 1][j - 1] + cost
  removeCost    = m[i - 1][j] + 1
  insertCost    = m[i][j - 1] + 1
  costs         = [replaceCost, removeCost, insertCost]
  m[i][j]       = min(costs)
  op[i][j]      = costs.index(m[i][j])

ops = []
i = len1
j = len2
while i != 0 or j != 0:

if op[i][j] == REMOVE or j == 0:
  ops.append('remove {}-th char {} of {}'.format(i,s1[i-1],s1))
  i = i - 1
elif op[i][j] == INSERT or i == 0:
  ops.append('insert {}-th char {} of {}'.format(j,s2[j-1],s2)) 
  j = j - 1
else:
  if m[i - 1][j - 1] < m[i][j]:
    fmt='replace {}-th char of {} ({}) with {}'
    ops.append(fmt.format(s1, i, s1[i - 1], s2[j - 1]))
i, j = i - 1, j - 1

return mlen1, ops

相关文章
|
3月前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
536 1
|
8月前
|
人工智能 运维 算法
基于 C# 深度优先搜索算法的局域网集中管理软件技术剖析
现代化办公环境中,局域网集中管理软件是保障企业网络高效运行、实现资源合理分配以及强化信息安全管控的核心工具。此类软件需应对复杂的网络拓扑结构、海量的设备信息及多样化的用户操作,而数据结构与算法正是支撑其强大功能的基石。本文将深入剖析深度优先搜索(Depth-First Search,DFS)算法,并结合 C# 语言特性,详细阐述其在局域网集中管理软件中的应用与实现。
203 3
|
4月前
|
运维 监控 算法
基于 Java 滑动窗口算法的局域网内部监控软件流量异常检测技术研究
本文探讨了滑动窗口算法在局域网流量监控中的应用,分析其在实时性、资源控制和多维分析等方面的优势,并提出优化策略,结合Java编程实现高效流量异常检测。
185 0
|
5月前
|
监控 算法 安全
基于 C# 基数树算法的网络屏幕监控敏感词检测技术研究
随着数字化办公和网络交互迅猛发展,网络屏幕监控成为信息安全的关键。基数树(Trie Tree)凭借高效的字符串处理能力,在敏感词检测中表现出色。结合C#语言,可构建高时效、高准确率的敏感词识别模块,提升网络安全防护能力。
145 2
|
8月前
|
机器学习/深度学习 存储 算法
18个常用的强化学习算法整理:从基础方法到高级模型的理论技术与代码实现
本文系统讲解从基本强化学习方法到高级技术(如PPO、A3C、PlaNet等)的实现原理与编码过程,旨在通过理论结合代码的方式,构建对强化学习算法的全面理解。
2148 10
18个常用的强化学习算法整理:从基础方法到高级模型的理论技术与代码实现
|
7月前
|
监控 算法 JavaScript
基于 JavaScript 图算法的局域网网络访问控制模型构建及局域网禁止上网软件的技术实现路径研究
本文探讨局域网网络访问控制软件的技术框架,将其核心功能映射为图论模型,通过节点与边表示终端设备及访问关系。以JavaScript实现DFS算法,模拟访问权限判断,优化动态策略更新与多层级访问控制。结合流量监控数据,提升网络安全响应能力,为企业自主研发提供理论支持,推动智能化演进,助力数字化管理。
204 4
|
7月前
|
存储 监控 算法
内网监控桌面与 PHP 哈希算法:从数据追踪到行为审计的技术解析
本文探讨了内网监控桌面系统的技术需求与数据结构选型,重点分析了哈希算法在企业内网安全管理中的应用。通过PHP语言实现的SHA-256算法,可有效支持软件准入控制、数据传输审计及操作日志存证等功能。文章还介绍了性能优化策略(如分块哈希计算和并行处理)与安全增强措施(如盐值强化和动态更新),并展望了哈希算法在图像处理、网络流量分析等领域的扩展应用。最终强调了构建完整内网安全闭环的重要性,为企业数字资产保护提供技术支撑。
235 2
|
8月前
|
存储 监控 算法
基于 Python 哈希表算法的局域网网络监控工具:实现高效数据管理的核心技术
在当下数字化办公的环境中,局域网网络监控工具已成为保障企业网络安全、确保其高效运行的核心手段。此类工具通过对网络数据的收集、分析与管理,赋予企业实时洞察网络活动的能力。而在其运行机制背后,数据结构与算法发挥着关键作用。本文聚焦于 PHP 语言中的哈希表算法,深入探究其在局域网网络监控工具中的应用方式及所具备的优势。
283 7
|
8月前
|
运维 监控 算法
基于 Python 迪杰斯特拉算法的局域网计算机监控技术探究
信息技术高速演进的当下,局域网计算机监控对于保障企业网络安全、优化资源配置以及提升整体运行效能具有关键意义。通过实时监测网络状态、追踪计算机活动,企业得以及时察觉潜在风险并采取相应举措。在这一复杂的监控体系背后,数据结构与算法发挥着不可或缺的作用。本文将聚焦于迪杰斯特拉(Dijkstra)算法,深入探究其在局域网计算机监控中的应用,并借助 Python 代码示例予以详细阐释。
222 6

热门文章

最新文章