【问题标题】:Dynamic programming and Divide and conquer动态规划和分而治之
【发布时间】:2012-02-17 09:55:06
【问题描述】:

我在阅读notes on Dynamic programming,遇到如下评论。

如果子问题不是独立的,即 子问题共享子子问题,然后分治算法重复解决公共问题 子问题。 因此,它所做的工作比必要的要多

这是什么意思?你能给我举个例子来说明上述观点吗?

【问题讨论】:

    标签: algorithm dynamic-programming divide-and-conquer


    【解决方案1】:

    作者提到了这样一个事实,即许多分而治之的算法都有相互重叠的子问题。例如,考虑这个非常简单的斐波那契实现:

    int Fibonacci(int n) {
        if (n <= 1) return n;
    
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
    

    如果你追踪计算斐波那契(4)的调用,我们得到

    • Fibonacci(4) 调用 Fibonacci(3) 和 Fibonacci(2)
    • Fibonacci(3) 调用 Fibonacci(2) 和 Fibonacci(1)
    • Fibonacci(2) 调用 Fibonacci(1) 和 Fibonacci(0)
    • Fibonacci(2)(另一个)调用 Fibonacci(1) 和 Fibonacci(0)
    • Fibonacci(1) 终止。
    • Fibonacci(1) 终止。
    • Fibonacci(1) 终止。
    • Fibonacci(0) 终止。
    • Fibonacci(0) 终止。

    换句话说,总共进行了 9 个函数调用,但这里只有 5 个唯一调用(斐波那契 0 到 4 包括在内)。如果递归调用在子问题之间共享而不是每次都从头开始重新计算,则该算法可以变得更加高效。这是动态规划背后的关键思想之一。

    要计算 Fn(第 n 个斐波那契数),上面的代码将进行总共 2Fn+1 - 1 次递归调用。由于斐波那契数以指数方式快速增长,因此这需要以指数方式进行的大量工作。但是,可以使用许多递归调用相同的事实来显着简化这一点。与其从 Fibonacci(4) 开始向下工作,不如从 Fibonacci(0) 开始向上工作。具体来说,我们将构建一个长度为 5 的表(我们称之为 FTable),并将其填写如下:

    • FTable[0] = 0
    • FTable[1] = 1

    现在,假设我们要计算 FTable[2]。这需要我们知道 FTable[0] 和 FTable[1],但我们已经知道了,因为它们在表中。因此我们可以设置

    • FTable[2] = 1

    使用类似的逻辑,我们可以从 FTable[2] 和 FTable[1] 计算 FTable[3]:

    • FTable[3] = 2

    FTable[2] 和 FTable[3] 中的 FTable[4]:

    • FTable[4] = 3

    请注意,我们如何通过以相反的顺序构建它们来避免进行大量重叠的递归调用!这现在可以在 O(n) 时间内计算斐波那契数,这比以前快得多。使用一些更高级的数学,我们可以做得比这更好,但这确实说明了为什么动态规划可以解决不可行的问题并使它们突然变得可行。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2017-02-24
      • 2011-09-03
      • 2013-08-01
      • 2012-11-12
      • 2013-11-09
      • 1970-01-01
      • 2012-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多