什么是动态规划?
它与递归、记忆等有什么不同?
我读过维基百科上关于它的文章,但我还是不太明白。
什么是动态规划?
它与递归、记忆等有什么不同?
我读过维基百科上关于它的文章,但我还是不太明白。
当前回答
动态规划是指使用过去的知识来更容易地解决未来的问题。
一个很好的例子是求解n=1,000,002的斐波那契数列。
这将是一个非常漫长的过程,但是如果我给出n=1,000,000和n=1,000,001的结果呢?突然之间,问题变得更容易控制了。
动态规划在字符串问题中被大量使用,例如字符串编辑问题。您解决问题的一个子集,然后使用该信息来解决更困难的原始问题。
使用动态编程,通常将结果存储在某种表中。当你需要一个问题的答案时,你可以参考表格,看看你是否已经知道它是什么。如果没有,你可以利用表格中的数据为自己找到答案提供一个垫脚石。
Cormen算法书中有一章是关于动态规划的。而且它在谷歌Books上是免费的!点击这里查看。
其他回答
记忆是指存储函数调用之前的结果(给定相同的输入,真正的函数总是返回相同的结果)。在存储结果之前,算法的复杂性并没有什么不同。
递归是函数调用自身的方法,通常使用较小的数据集。由于大多数递归函数可以转换为类似的迭代函数,这对算法复杂性也没有影响。
动态规划是解决较容易解决的子问题,并由此建立答案的过程。大多数DP算法将处于贪婪算法(如果存在的话)和指数算法(枚举所有可能性并找到最佳的一个)之间的运行时间。
DP算法可以用递归来实现,但它们不必这样做。 DP算法不能通过记忆来加速,因为每个子问题只被解决(或“solve”函数被调用)一次。
动态规划是指使用过去的知识来更容易地解决未来的问题。
一个很好的例子是求解n=1,000,002的斐波那契数列。
这将是一个非常漫长的过程,但是如果我给出n=1,000,000和n=1,000,001的结果呢?突然之间,问题变得更容易控制了。
动态规划在字符串问题中被大量使用,例如字符串编辑问题。您解决问题的一个子集,然后使用该信息来解决更困难的原始问题。
使用动态编程,通常将结果存储在某种表中。当你需要一个问题的答案时,你可以参考表格,看看你是否已经知道它是什么。如果没有,你可以利用表格中的数据为自己找到答案提供一个垫脚石。
Cormen算法书中有一章是关于动态规划的。而且它在谷歌Books上是免费的!点击这里查看。
动态规划是一种在递归算法中避免多次计算同一子问题的技术。
让我们以斐波那契数为例:找到定义的第n个斐波那契数
Fn = Fn-1 + Fn-2, F0 = 0, F1 = 1
递归
最明显的方法是递归:
def fibonacci(n):
if n == 0:
return 0
if n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
动态规划
自顶向下——记忆
递归做了很多不必要的计算,因为给定的斐波那契数将被计算多次。一个简单的改进方法是缓存结果:
cache = {}
def fibonacci(n):
if n == 0:
return 0
if n == 1:
return 1
if n in cache:
return cache[n]
cache[n] = fibonacci(n - 1) + fibonacci(n - 2)
return cache[n]
自底向上
更好的方法是通过按正确的顺序计算结果来摆脱递归:
cache = {}
def fibonacci(n):
cache[0] = 0
cache[1] = 1
for i in range(2, n + 1):
cache[i] = cache[i - 1] + cache[i - 2]
return cache[n]
我们甚至可以使用常数空间,在整个过程中只存储必要的部分结果:
def fibonacci(n):
fi_minus_2 = 0
fi_minus_1 = 1
for i in range(2, n + 1):
fi = fi_minus_1 + fi_minus_2
fi_minus_1, fi_minus_2 = fi, fi_minus_1
return fi
如何应用动态规划? 找出问题中的递归。 自顶向下:将每个子问题的答案存储在一个表中,以避免重新计算。 自底向上:找到评估结果的正确顺序,以便在需要时获得部分结果。
动态规划通常适用于具有固有的从左到右顺序的问题,如字符串、树或整数序列。如果单纯递归算法不能多次计算同一子问题,动态规划就没有帮助。
我做了一个问题集合来帮助理解逻辑:https://github.com/tristanguigue/dynamic-programing
我对动态规划(一种针对特定类型问题的强大算法)也非常陌生。
简单地说,只需将动态规划视为使用前面知识的递归方法
以前的知识在这里是最重要的,跟踪你已经有的子问题的解决方案。
下面是来自维基百科的dp最基本的例子
求斐波那契数列
function fib(n) // naive implementation
if n <=1 return n
return fib(n − 1) + fib(n − 2)
让我们用n = 5来分解函数调用
fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
特别地,fib(2)从零开始计算了三次。在较大的示例中,需要重新计算fib(或子问题)的更多值,从而形成指数时间算法。
现在,让我们尝试将我们已经找到的值存储在一个数据结构(比如Map)中
var m := map(0 → 0, 1 → 1)
function fib(n)
if key n is not in map m
m[n] := fib(n − 1) + fib(n − 2)
return m[n]
这里我们将子问题的解决方案保存在地图中,如果我们还没有的话。这种我们已经计算过的保存值的技术被称为记忆化。
最后,对于一个问题,首先尝试找到状态(可能的子问题,并尝试考虑更好的递归方法,以便将以前的子问题的解决方案用于进一步的子问题)。
动态规划
定义
动态规划(DP)是求解问题的一种通用算法设计技术 带有重叠子问题的问题。这项技术是美国人发明的 数学家理查德·贝尔曼在20世纪50年代。
关键理念
其关键思想是保存重叠的小问题的答案,以避免重新计算。
动态规划属性
使用较小实例的解决方案求解实例。 较小实例的解决方案可能需要多次使用, 所以把结果存储在一个表中。 因此,每个较小的实例只解决一次。 额外的空间被用来节省时间。