算法简介:动态规划

假设您正在使用适当的一系列输入进行一些计算。在每个实例中都会进行一些计算以得到一些结果。当您提供相同的输入时,您不知道会得到相同的输出。这就像您正在重新计算之前已经计算好的特定结果一样。

那问题出在哪呢?问题是你宝贵的时间被浪费了。您可以通过保留映射先前计算结果的记录来轻松解决这个问题。比如使用合适的数据结构。例如,您可以将输入输出作为键值对存储起来(映射的一部分)。

那些遗忘过去的人注定要重蹈覆辙。~动态规划

现在通过分析这个问题,我们可以将其新的输入(或不在数据结构中的输入)与其对应的输出存储下来,或者在字典中查找该输入并返回对应的输出。这样当您进行一些计算时,您可以检查该输入是否存在于该数据结构中,如果存在您可以直接获得结果。我们将与这种方法相关的技巧称作动态规划

深入了解动态规划

简而言之,我们可以说动态规划主要用于优化问题,我们希望找到最优解。

在某种情况下,就像有重复出现的子问题,而这些子问题又具有自己的较小子问题。动态规划建议只解决每个较小的子问题一次,而不是一次又一次地尝试解决那些重新出现的子问题。然后我们将结果记录在一个表格中,在之后的计算中可以把这些记录作为问题的原始解

例如,斐波那契数字 0,1,1,2,3,5,8,13,…有一个简单的描述,其中每个数字都与其之前紧邻的两个数字相关。如果F(n)是这个系列的第n项,那么我们有F(n) = F(n-1) + F(n-2)。这称为递归公式递归关系。它需要计算好前面的项才能计算出后面的项。

大多数动态规划问题可以分为两类:

  1. 优化问题。
  2. 组合问题。

优化问题希望您选择一个可行的解决方案,以使所需函数的值最小化或最大化。组合问题希望您弄清楚做某事的方法的数量或某些事件发生的概率。

解决方法:自上而下VS自下而上

解决问题的方法主要有以下两种:

自上而下:您从顶部开始,通过分解问题来解决问题。如果您发现问题已经解决,则只需返回保存的答案。这称为记忆存储。

自下而上:您直接开始解决较小的子问题,然后向上推导出一个大问题的最终解决方案。在这个过程中,保证在解决问题之前先解决子问题。这可以称为制表表格填充算法

至于迭代与递归,自下而上使用迭代,自上而下使用递归。

                 图像中显示的可视化在理论上可能不完全正确,但这是一种可以理解的展示方式

下面是普通方法与动态规划方法之间的比较。您可以通过两者的时间复杂度看到差异。

存储化:别忘了

Jeff Erickson在他的笔记中这样描述斐波那契数:

递归算法之所以慢的原因是它一遍又一遍地计算相同的斐波那契数。

                                 来自 Jeff Erickson 的笔记抄送:http: //jeffe.cs.illinois.edu/

我们可以通过记录递归调用的结果来加速递归算法,这样在之后需要这些结果时就不必重新算了

存储化是指缓存和重用先前计算结果的技术。

如果您使用存储化来解决问题,您可以通过维护已经解决的子问题的映射来实现(正如我们之前讨论过的键值对映射)。这种情况下你是“自上而下”的,因为你首先解决了“上层”问题(通常会向下递归解决子问题)。

用于存储化的伪代码

因此,使用递归,我们使用额外的内存开销(即此处查找)来存储结果。如果查找中存储了一个值,我们直接返回它,或者将它添加到特定索引中。

请记住,相对于制表方法,需要权衡额外的内存开销。

但是,如果您想要更多可视化的记忆,我建议您查看此视频

                                                                 以自上而下的方式。

制表:以表格形式填充

但是一旦我们看到数组(存储解决方案)是如何填充的,我们就可以用一个简单的循环来替换递归,该循环有意地按顺序填充数组,而不是依靠复杂的递归来“意外”为我们完成它。

                                 来自 Jeff Erickson 的笔记抄送:http: //jeffe.cs.illinois.edu/

制表以“自下而上”的方式进行。它更直接,会计算所有值。但它需要的开销更少,因为它不必维护映射,并以表格形式存储数据。它还可能计算不必要的值。如果您只想计算问题的所有值,则可以使用此方法。

制表伪代码:

                                                         斐波那契树的伪代码

正如您在图像中看到的伪代码(右侧),它会进行迭代(即循环到数组的末尾)。它从 fib(0),fib(1),fib(2),... 开始,因此使用制表方法,我们可以消除递归,只需要通过遍历元素返回结果。

追根溯源

理查德贝尔曼是这个概念的提出者。他在 1950 年代中期为兰德公司工作时提出了这个想法。他选择“动态规划”这个名字的原因是为了隐藏他为这项研究所做的数学工作。他担心他的老板会反对或不喜欢任何形式的数学研究。

好的,所以“编程”这个词只是一个参考,以表明这是一种老式的计划或调度方式,通常是通过渐进式填充表格(以动态方式而不是线性方式)而不是一次全部填充。

结束语

就到这里了。这是我去年开始的算法系列的第 2 部分。在我之前的帖子中,我们讨论了什么是搜索和排序算法。很抱歉我不能在更短的时间内完成这个。但我愿意在接下来的几个月里让事情变得更快。

希望你喜欢它,我很快就会在该系列中添加第三个。快乐编码!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值