memoization和动态编程有什么区别?

时间:2011-05-31 08:28:32

标签: dynamic-programming terminology difference memoization

memoization和动态编程有什么区别?我认为动态编程是memoization的一个子集。是不是?

9 个答案:

答案 0 :(得分:306)

  

记忆和动态编程之间有什么区别?

Memoization 是一个描述优化技术的术语,您可以在其中缓存先前计算的结果,并在需要再次进行相同计算时返回缓存结果。

动态编程是一种迭代解决递归性问题的技术,适用于子问题的计算重叠时。

动态编程通常使用制表实现,但也可以使用memoization实现。你可以看到,没有一个是另一个的“子集”。


一个合理的后续问题是:制表(典型的动态编程技术)和记忆之间有什么区别?

当您使用制表解决动态编程问题时,您可以解决问题“自下而上”,即首先解决所有相关的子问题,通常是填写 n - 维表。根据表格中的结果,然后计算“顶部”/原始问题的解决方案。

如果使用memoization来解决问题,可以通过维护已经解决的子问题的映射来实现。在你首先解决“顶级”问题的意义上,你可以“自上而下”(这通常是为了解决子问题而重新解决)。

来自 here 的好幻灯片(链接已经死了,幻灯片仍然很好):

  
      
  • 如果所有子问题必须至少解决一次,那么自下而上的动态编程算法通常会以常数因子优于自上而下的记忆算法      
        
    • 没有递归开销,维护表
    • 的开销更少   
    • 有一些问题可以利用动态编程算法中的表访问的常规模式来进一步减少时间或空间要求
    •   
  •   
  • 如果子问题空间中的某些子问题根本无法解决,那么备忘解决方案的优势就是只解决那些绝对需要的子问题
  •   

其他资源:


这已被重写为文章here

答案 1 :(得分:41)

  

动态编程是一种解决给定问题的算法范例   通过将其分解为子问题并存储结果来解决复杂问题   子问题,以避免再次计算相同的结果。

http://www.geeksforgeeks.org/dynamic-programming-set-1/

记忆是一种简单的方法来跟踪以前解决的解决方案(通常实现为散列键值对,而不是通常基于数组的制表),以便在再次遇到它们时不会重新计算它们。它可以用于自下而上或自上而下的方法。

关于memoization vs tabulation,请参阅this discussion

因此,动态编程是一种通过解决递归关系/递归并通过制表或记忆存储以前找到的解决方案来解决某些类问题的方法。记忆是一种跟踪先前解决问题的解决方案的方法,可以与任何具有给定输入集的唯一确定性解决方案的函数一起使用。

答案 2 :(得分:12)

动态编程通常称为Memoization!

  1. 记忆是自上而下的技术(通过分解来开始解决给定的问题)和动态编程是一种自下而上的技术(从琐碎的子问题开始解决,直到给定的问题)< / p>

  2. DP通过从基本案例开始查找解决方案并向上运行。 DP解决了所有子问题,因为它是自下而上的

      

    与Memoization不同,它只解决了所需的子问题

  3. DP有可能将指数时暴力解决方案转换为多项式时间算法。

  4. DP可能效率更高,因为它的迭代

      

    相反,Memoization必须支付由递归引起的(通常很大的)开销。

  5. 更简单一点, Memoization使用自上而下的方法来解决问题,即它从核心(主要)问题开始,然后将其分解为子问题并类似地解决这些子问题。在这种方法中,相同的子问题可能多次发生并消耗更多的CPU周期,因此增加了时间复杂度。而在动态编程中,相同的子问题不会多次解决,但先前的结果将用于优化解决方案。

答案 3 :(得分:7)

(1)Memoization和DP,概念,实际上是一回事。因为:考虑DP的定义:&#34;重叠的子问题&#34; &#34;和最佳子结构&#34;。 Memoization完全拥有这两个。

(2)Memoization是DP,堆栈溢出的风险是递归很深。 DP自下而上没有这种风险。

(3)Memoization需要一个哈希表。所以额外的空间和一些查找时间。

所以回答这个问题:

- 概念上,(1)意味着它们是相同的。

- 考虑到(2),如果你真的想要,memoization是DP的一个子集,从某种意义上说,DP可以解决通过memo化解决的问题,但DP解决的问题可能无法通过memoization解决(因为它可能堆栈溢出)。

- 考虑到(3),他们在表现方面存在细微差别。

答案 4 :(得分:6)

来自维基百科:

<强>记忆化

  

在计算中,memoization是一种主要使用的优化技术   通过函数调用避免重复来加速计算机程序   计算先前处理的输入的结果。

动态编程

  

在数学和计算机科学中,动态规划是一种方法   通过将它们分解为更简单来解决复杂问题   子问题。

当将问题分解为更小/更简单的子问题时,我们经常遇到相同的子问题,因此我们使用Memoization来保存先前计算的结果,因此我们不需要重复它们。

动态编程经常遇到使用memoization有意义的情况,但是你可以使用任何一种技术而不必使用另一种技术。

答案 5 :(得分:4)

记忆和动态编程只能解决单个子问题一次。

记忆化使用递归并自上而下进行,而动态编程朝相反的方向移动,从而解决了自下而上的问题。

下面是一个有趣的类比-

自上而下-首先,您说我将接管世界。你会怎么做?你说我先接管亚洲。你会怎么做?我将首先接管印度。我将成为德里等的首席部长。

自下而上-您说我将成为德里的CM。然后将接管印度,然后接管亚洲所有其他国家,最后我将接管世界。

答案 6 :(得分:2)

只需考虑两种方式,

  1. 我们将较大的问题分解为较小的子问题-自上而下的方法。
  2. 我们从最小的子问题开始,再到更大的问题-自下而上的方法。

备忘录中,我们使用(1.),其中将每个函数调用保存在缓存中,然后从那里进行回调。由于涉及到递归调用,所以它有点贵。

动态编程中,我们继续使用(2.),在该表中我们维护一个表,通过使用表中保存的数据(通常称为dp-table)解决子问题,从而解决了问题。

注意:

  • 两者都适用于子问题重叠的问题。

  • 由于递归函数调用过程中涉及的开销,因此对DP的存储执行效果相对较差。

  • 渐近时间复杂度不变。

答案 7 :(得分:1)

我想和一个example一起去;

问题:

  

您正在爬楼梯。到达顶部需要n步。

     

每次您可以爬1步或2步。有多少种不同的方式   你可以爬到山顶吗?

enter image description here

带记忆的递归

通过这种方式,我们在备忘录数组的帮助下修剪(从树或灌木中去除多余的材料)递归树,并将递归树的大小减小到nn。

public class Solution {
    public int climbStairs(int n) {
        int memo[] = new int[n + 1];
        return climb_Stairs(0, n, memo);
    }
    public int climb_Stairs(int i, int n, int memo[]) {
        if (i > n) {
            return 0;
        }
        if (i == n) {
            return 1;
        }
        if (memo[i] > 0) {
            return memo[i];
        }
        memo[i] = climb_Stairs(i + 1, n, memo) + climb_Stairs(i + 2, n, memo);
        return memo[i];
    }
}

动态编程

如我们所见,该问题可以分解为子问题,并且包含最优子结构属性,即可以根据子问题的最优解有效地构造其最优解,我们可以使用动态编程来解决该问题。

public class Solution {
    public int climbStairs(int n) {
        if (n == 1) {
            return 1;
        }
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

示例取自https://leetcode.com/problems/climbing-stairs/

答案 8 :(得分:0)

动态编程中,

  • 没有递归开销,因此维护表的开销更少。
  • 可以使用常规的表访问模式来减少时间或空间需求。

记忆中,

  • 一些子问题不需要解决。