动态编程……更好的方法?

动态编程……更好的方法?

这是一个编程概念,在我学习的时候给了我很大的困难。我的编程风格非常直观,因为我必须先将问题可视化才能解决它。动态编程对这种方法或任何一种直观的理解都非常抗拒。事实上,即使是解决方案也很难让您完全理解(我们将探讨原因)。

好吧,我会争辩说,您在网上找到的大多数解决方案和方法都依赖于成功生成正确答案的预烘焙逻辑,但未能揭示动态编程的实际工作原理。我会争辩说,最佳解决方案(最适合时间和空间复杂性)实际上是最容易理解的。

公认的做法是在这些巨大的数组中携带解决方案并返回最后一个元素。我可能花了一天的时间试图弄清楚你为什么要这样做。好吧,事实证明它除了增加内存复杂性和混淆每个试图理解动态编程实际工作原理的人之外什么也没做。

然后是递归方法。我不了解你,但对我来说,递归在图(和树)遍历中非常有意义,但是动态编程呢?我发现这不仅无法可视化,而且在时间和空间复杂度方面也恰好是最不理想的解决方案。是的,它可以通过缓存来改进,但自下而上的迭代解决方案总是更好(正如我们将看到的,也更容易理解)。

我将用一个非常简单的例子来演示。求下列数组的最大顺序和:

[-1, 2, 3, 4, -2]

这种情况下的解是 9(2、3、4 的总和)。现在,蛮力方法非常简单;只需找到所有可能的顺序子数组的最大总和……显然这不是我们想要的解决方案,因为时间复杂度是二次的。那么我们如何形象化呢?

需要认识到的一个关键点是,动态规划是一种用于解决可以分解为子问题的问题的技术。那么第一个子问题是什么?

[-1, 2]

这是第一个子问题。而且我们总是想向后评估动态规划问题!由于我们追求的是线性 O(n) 解决方案,因此在构建循环时,我们必须从 2 (i=1) 开始,因为我们将针对 -1 (i-1) 进行评估。为什么不反过来呢?好吧,如果您考虑一下,

所以让我们解决这个子问题来构建我们的逻辑。如果我们考虑 [-1,2],最大顺序和为 2。我们如何通过算法解决这个问题?

2 > -1+2 > -1

2,-1+2 和 -1 之间的最大值是 2。因此,前进的解决方案是保存(或缓存)2 并在下一次迭代中对其进行评估。好的,现在有趣的部分......我们如何想象这个?我称之为级联方法:

仔细看看我们在做什么。第一个子问题的解决方案是 2,我们将其存储在缓存变量中。现在,继续 i=2 (3),我们应用相同的逻辑,我们的缓存值变为 5(因为 3+2 大于 2 且大于 3)。移动到 i=3 (4),我们的逻辑给了我们 9,最后移动到 i=4 (-2),我们的逻辑给了我们 7 (-2 + 9)。并且由于 7 小于 9,所以它没有被设置为我们的缓存变量。

我们的循环结束,剩下 9 个……正确的解决方案。如您所见,没有理由在我们的缓存变量中携带多个值。没有逻辑理由将 2,5,9 存储在数组中并返回最后一个值 (9)。在这一点上应该是相当明显的,但大多数公认的解决方案都会做到这一点。

因此,如果您在解读动态编程解决方案时遇到困难,您必须知道这些解决方案不必要地复杂且难以理解。在我看来,要真正理解动态编程,我们必须知道我们正在缓存什么以及为什么(并且只缓存必要的内容)。在我的下一篇文章中,我们将对此进行编码并查看一些更难的示例。

alexpokho.xyz

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/33090/14371308

posted @ 2022-09-13 08:14  哈哈哈来了啊啊啊  阅读(21)  评论(0编辑  收藏  举报