假设您正在使用适当的一系列输入进行一些计算。在每个实例中都会进行一些计算以得到一些结果。当您提供相同的输入时,您不知道会得到相同的输出。这就像您正在重新计算之前已经计算好的特定结果一样。
那问题出在哪呢?问题是你宝贵的时间被浪费了。您可以通过保留映射先前计算结果的记录来轻松解决这个问题。比如使用合适的数据结构。例如,您可以将输入输出作为键值对存储起来(映射的一部分)。
那些遗忘过去的人注定要重蹈覆辙。~动态规划
现在通过分析这个问题,我们可以将其新的输入(或不在数据结构中的输入)与其对应的输出存储下来,或者在字典中查找该输入并返回对应的输出。这样当您进行一些计算时,您可以检查该输入是否存在于该数据结构中,如果存在您可以直接获得结果。我们将与这种方法相关的技巧称作动态规划。
深入了解动态规划
简而言之,我们可以说动态规划主要用于优化问题,我们希望找到最优解。
在某种情况下,就像有重复出现的子问题,而这些子问题又具有自己的较小子问题。动态规划建议只解决每个较小的子问题一次,而不是一次又一次地尝试解决那些重新出现的子问题。然后我们将结果记录在一个表格中,在之后的计算中可以把这些记录作为问题的原始解。
例如,斐波那契数字 0,1,1,2,3,5,8,13,…
有一个简单的描述,其中每个数字都与其之前紧邻的两个数字相关。如果F(n)
是这个系列的第n项,那么我们有F(n) = F(n-1) + F(n-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 部分。在我之前的帖子中,我们讨论了什么是搜索和排序算法。很抱歉我不能在更短的时间内完成这个。但我愿意在接下来的几个月里让事情变得更快。
希望你喜欢它,我很快就会在该系列中添加第三个。快乐编码!