是动态编程回溯缓存

Moh*_*han 11 theory algorithm dynamic-programming backtracking

我一直想知道这件事.没有书明确说明这一点.

回溯正在探索所有可能性,直到我们发现一种可能性无法引导我们找到可能的解决方案,在这种情况下我们放弃它.

据我所知,动态编程的特点是重叠的子问题.那么,动态编程是否可以表示为缓存回溯(对于以前探索过的路径)?

谢谢

Gas*_*ssa 8

这是动态编程的一个方面,但还有更多内容.

对于一个简单的例子,采取斐波那契数字:

F (n) =
        n = 0:  0
        n = 1:  1
        else:   F (n - 2) + F (n - 1)
Run Code Online (Sandbox Code Playgroud)

我们可以调用上面的代码"回溯"或"递归".让我们将其转换为"使用缓存进行回溯"或"使用memoization进行递归":

F (n) =
       n in Fcache:  Fcache[n]
       n = 0:  0, and cache it as Fcache[0]
       n = 1:  1, and cache it as Fcache[1]
       else:  F (n - 2) + F (n - 1), and cache it as Fcache[n]
Run Code Online (Sandbox Code Playgroud)

不过,还有更多.

如果可以通过动态编程解决问题,则存在状态和它们之间的依赖关系的有向非循环图.有一个国家对我们感兴趣.还有基本状态,我们立即知道答案.

  • 我们可以从我们感兴趣的顶点到它的所有依赖关系,从它们到它们依赖的所有依赖关系等遍历那个图形,停止在基本状态进一步分支.这可以通过递归来完成.

  • 有向无环图可以视为顶点上的部分顺序.我们可以在拓扑上对该图进行排序,并按排序顺序访问顶点.此外,您可以找到一些与您的部分订单一致的简单订单.

还要注意,我们经常可以在状态上观察一些结构.例如,状态通常可以表示为整数的整数或元组.因此,我们可以预先分配一个更容易和更快使用的常规数组,而不是使用通用缓存技术(例如,关联数组来存储状态 - >值对).


回到我们的Fibonacci例子,偏序关系只是状态n >= 2取决于状态n - 1n - 2.基本状态是n = 0n = 1.这个顺序关系一致的一个简单的订单总额的自然顺序:0,1,2,....以下是我们的开始:

Preallocate array F with indices 0 to n, inclusive
F[0] = 0
F[1] = 1
Run Code Online (Sandbox Code Playgroud)

好的,我们有命令访问各州.现在,什么是"访问"?还有两种可能性:

(1)"Backward DP":当我们访问一个状态时u,我们会查看它的所有依赖关系v并计算该状态的答案u:

for u = 2, 3, ..., n:
    F[u] = F[u - 1] + F[u - 2]
Run Code Online (Sandbox Code Playgroud)

(2)"转发DP":当我们访问一个州时u,我们会查看所有v依赖它并在以下各个州中占据u的州v:

for u = 1, 2, 3, ..., n - 1:
    add F[u] to F[u + 1]
    add F[u] to F[u + 2]
Run Code Online (Sandbox Code Playgroud)

请注意,在前一种情况下,我们仍然直接使用Fibonacci数的公式.然而,在后一种情况下,命令性代码不能通过数学公式容易地表达.但是,在某些问题上,"前向DP"方法更直观(现在没有好的例子;任何愿意贡献它的人?).


动态编程的另一个用途是难以表达为回溯:Dijkstra的算法也可以被认为是DP.在算法中,我们通过向顶点添加顶点来构造最优路径树.当我们添加一个顶点时,我们使用的事实是它的整个路径 - 除了路径中的最后一个边缘 - 已经知道是最优的.因此,我们实际上使用了一个子问题的最优解决方案 - 这正是我们在DP中所做的事情.仍然,我们事先不知道向树添加顶点的顺序.


Chr*_*aki 5

不,或者更确切地说.

在回溯中,你往下走,然后备份每条路径.但是,动态编程是自下而上的,因此您只能获得备份部分而不是原始的部分.此外,动态编程的顺序首先是广度,而回溯通常是深度优先.

另一方面,memoization(动态编程非常接近的表兄)经常用作缓存的回溯,就像你描述的那样.

  • 记忆在很多书中被称为自上而下的DP. (4认同)
  • 是的,memoization是一种实现DP的方法(也是一个很好的方法,因为表格会以正确的顺序"自动填充") (2认同)
  • 据我了解,记忆化和“正确的”动态编程在复杂性和值计算的相对顺序方面是相同的;然而,记忆虽然看起来很幼稚,但可能会跳过一些子问题的评估。 (2认同)