动态规划(Dynamic programming)
是另一种经常被用来解决最优化问题
的技术,它有点类似于分治算法。- 当我们把问题分解成
子问题
,并且每个子问题都只出现一次
的时候,简单的分治算法会非常有效。但是,这种策略对于递归版本的斐波那契算法是非常低效的,这是因为它会让我们对相同的子问题进行多次重复的计算
。 动态规划
解决这个问题的方案是:把每个子问题的答案都存起来
,并在需要的时候重用这个存起来的答案,而不是去重新计算这个子问题。- 我们的迭代版本的
斐波那契算法
就可以被归类为动态规划算法
。作为提醒,下面这个代码片段是迭代版本的斐波那契函数
,它的运行时间是Θ (n):
def fib(n):
curr = 1 # 当前数字
prev = 1 # 前面一个数字
for i in range(n - 2):
curr, prev = curr + prev, curr
return curr
x=[fib(i) for i in range(10)]
print(x) #[1, 1, 1, 2, 3, 5, 8, 13, 21, 34]
这是一个非常简单的动态规划
的例子,因为在这里我们只需要把最近的两个子问题的答案存储起来就行了,而且很容易把子问题的答案组合起来从而得到当前问题的答案。
和分治算法一样,动态规划算法的一个关键概念是:
确定如何把子问题的解组合成初始问题的解。在某些情况下,我们只需要分析一个或者两个子问题就能解决初始问题了。在其他情况下,我们需要多分析一些子问题。在需要分析多个子问题的情况下,动态规划算法非常常见,因为我们在解决初始问题的时候经常需要多次分析相同的子问题。这个概念通常被称为重叠子问题
(overlappingsubproblem)。
有没有重叠子问题并不是决定应不应该用动态规划技术的唯一标准
。最优化问题必须满足的另一个重要标准是:初始问题的解决方案必须包含子问题的最优解。在设计动态规划算法的时候,确定如何将子问题的最优解组合起来得到初始问题的最优解通常来说是最困难的任务
。
虽然分治算法通常是通过递归来实现的,但是动态规划算法通常会从基本案例问题开始,然后通过它来确定解决更大问题的方法,直到找到初始问题的解决方案。因此,动态规划算法通常不会使用递归来实现
。恰恰相反,典型的动态规划算法会使用迭代的解决方案,这个解决方案会把子问题的解存在表里,然后直接在表里填写出更大问题的解,直到计算出初始问题的答案。我们将会仔细研究一个可以用动态规划来解决的问题,以及为另一个可以通过动态规划来解决的问题提供基本的算法。
为什么叫做动态规划?
名不副实
动态规划什么时候可以用?
子问题的最优解就是整体问题的最优解的时候.(因此并不是所有问题都有这个特性)
斐波那契的递归算法子问题重复计算体现在哪里?
比如fib(n-1) 不能利用fib(n-2)的计算结果
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
x=[fib(i) for i in range(10)]
print(x) #[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
总结
动态规划
用于求解最优化问题
动态规划
这名字起的不好,看不出来干什么的斐波那契
的迭代算法可以视为动态规划的一种案例- 关键是把
子问题
的结果存起来