我刚读过this short post about mental models for Recursive Memoization vs Dynamic Programming, written by professor Krishnamurthi。在其中,Krishnamurthi表示作为递归树的memoization的自上而下结构,以及DP的自下而上结构作为DAG,其中源顶点是第一个 - 可能是最小的 - 子问题已解决,并且sink vertex是最终计算(本质上该图与上述递归树相同,但所有边都被翻转)。很公平;这很有道理。
无论如何,最后,他给读者一个心理锻炼:
Memoization是自上而下深度优先计算的优化 得到答案。 DP是自下而上,广度优先的优化 计算答案。
我们应该自然地问,
怎么样
- 自上而下,广度优先
- 自下而上,深度优先
他们适合哪里 通过权衡来避免重新计算的技术空间 时间空间?
- 我们已经有了他们的名字吗?如果是,那么?或
- 我们是否遗漏了一两个重要的伎俩?或者
- 我们之所以没有这些名字吗?
但是,他停在那里,没有对这些问题发表意见。
我迷路了,但这里有:
我的解释是,自上而下,广度优先计算将需要为每个函数调用单独的过程。自下而上,深度优先的方法会以某种方式将最终解决方案拼凑在一起,因为每条迹线都会到达"接收顶点"。解决方案最终会加起来#34;所有电话都打到正确的答案。
我有多远?有谁知道他的三个问题的答案?
答案 0 :(得分:4)
让我们分析两个图中边缘的含义。从子问题 a 到 b 的边缘表示 b 的解决方案用于计算 a 的关系并且必须在它之前解决。 (另一种情况则相反。)
是否会想到拓扑排序?
进行拓扑排序的一种方法是执行深度优先搜索,并在离开每个节点的路上进行处理。这基本上是递归记忆的作用。你从每个子问题深入到深度优先,直到你遇到一个你尚未解决的问题(或者你没有访问过的节点)并解决它。
动态规划,或自下而上 - 广泛的第一个问题解决方法涉及解决较小的问题并从中构建较大问题的解决方案。这是进行拓扑排序的另一种方法,您可以访问度为0的节点,对其进行处理,然后将其删除。在DP中,最小的问题首先得到解决,因为它们具有较低的度数。 (较小的是对手头的问题是主观的。)
这里的问题是生成必须解决子问题集的序列。自上而下的广度优先和自下而上的深度优先不能做到这一点。 自上而下广度优先仍将最终执行与深度优先计数器部分非常相似的操作,即使该进程被分隔为线程。有一个必须解决问题的顺序。 自下而上深度优先方法可以部分地解决问题,但最终结果仍然类似于广度第一计数器部分。子问题将以类似的顺序解决。
鉴于这些方法几乎没有其他方法的改进,不能很好地与类比进行翻译,并且实施起来很繁琐,它们还没有很好地建立起来。
答案 1 :(得分:3)
@AndyG's comment在这里非常重要。我也喜欢@shebang's answer,但这里有一个在这种情况下直接回答这些问题,而不是通过简化为另一个问题。
目前还不清楚自上而下的广度第一解决方案会是什么样子。但即使你以某种方式暂停计算而不进行任何子计算(人们可以设想可能实现的各种基于延续的方案),也没有意义这样做,因为会有子问题的共享。 / p>
同样,不清楚自下而上的深度 - 第一解决方案是否可以解决问题。如果你自下而上进行计算,但是其他子问题的解决方案还没有准备好等待,那么你就会计算垃圾。
因此,自上而下,广度优先没有任何好处,而自下而上,深度优先甚至不提供解决方案。