递归可以是动态编程吗?

时间:2013-10-03 02:50:41

标签: recursion dynamic-programming fibonacci

我被要求使用动态编程来解决问题。我对动态编程的构成有不同的注释。我认为它需要一种“自下而上”的方法,首先解决最小的问题。

我有一点与之相反的信息是,如果相同的子问题不止一次被解决,某些东西是否可以是动态编程,就像在递归中一样。

例如。对于Fibonacci,我可以有一个递归算法:

RecursiveFibonacci(n)
if (n=1 or n=2)
    return 1
else
    return RecursiveFibonacci(n-1) + RecursiveFibonacci(n-2)

在这种情况下,可以反复解决相同的子问题。这是否会使动态编程?也就是说,如果我想要动态编程,我是否必须避免解决子问题,例如使用长度为n的数组并将解决方案存储到每个子问题(数组的第一个索引是1,1 ,2,3,5,8,13,21)?

Fibonacci(n)
F1 = 1
F2 = 1
for i=3 to n
    Fi=Fi-1 + Fi-2
return Fn

2 个答案:

答案 0 :(得分:2)

通常可以使用递归公式简洁地描述动态程序。

但是如果用简单的递归计算机程序实现它们,这些通常是低效的,正是因为你提出的原因:重复相同的计算。 Fibonacci是重复计算的一个例子,虽然它不是动态程序。

有两种方法可以避免重复。

  1. 记忆化。这里的想法是将为每组参数计算的答案缓存到递归函数,并在存在时返回缓存的值。

  2. 自下而上的表格。在这里,您“展开”递归,以便将小于i的级别的结果组合到级别i的结果。这通常被描述为填写表格,其中级别为行。

  3. 任何DP算法都暗示了其中一种方法。如果重复计算,则算法不是DP。所以你的问题的答案是肯定的。

    所以一个例子......让我们尝试更换c美分的问题,因为你的硬币值为v_1,v_2,... v_n,使用最少数量的硬币。

    设N(c)是制作c美分所需的最小硬币数。然后是一个递归公式

    N(c) = 1 + min_{i = 1..n} N(c - v_i) 
    

    基本情况为N(0)= 0且N(k)= inf,k <0。

    要记住这个,只需要一个将c映射到N(c)的哈希表。

    在这种情况下,“表格”只有一个维度,很容易填写。假设我们有值为1,3,5的硬币,那么N表以

    开头
    • N(0)= 0,初始条件。
    • N(1)= 1 + min(N(1-1),N(1-3),N(1-5)= 1 + min(0,inf,inf)= 1
    • N(2)= 1 + min(N(2-1),N(2-3),N(2-5)= 1 + min(1,inf,inf)= 2
    • N(3)= 1 + min(N(3-1),N(3-3),N(3-5)= 1 + min(2,0,inf)= 1

    你明白了。你总是可以从N(d)计算N(c),d&lt; c以这种方式。

    在这种情况下,您只需记住最后5个值,因为这是最大的硬币值。大多数DP都很相似。获取下一个表只需要几行表。

    对于递归表达式中的k个独立变量,该表是k维的。

答案 1 :(得分:0)

我们考虑一种动态编程方法来解决问题

  1. 重叠子问题
  2. 最佳子结构

用简单的话来说,动态编程有两个方面,它们是自上而下和自下而上的方法。

在您的情况下,如果您要讨论递归,则这是自顶向下的方法。 在自上而下的方法中,我们将尝试编写递归解决方案或蛮力解决方案并记录结果,以便在出现类似的子问题时尝试使用该结果,因此它是 brute-force +备忘录。我们可以通过简单的递归关系来实现这种蛮力方法。