研究生课程 算法分析-回溯法和分支限界法

回溯法(backtrack)

有许多问题,当需要找出它的解集或者要求回答什么解是满足某些约束条件的最佳解时,往往要使用回溯法。回溯法的基本做法是 搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。这种方法适用于解一些组合数相当大的问题。

回溯法在问题的解空间树中,按 深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。

算法过程

回溯法会用深度优先的方式搜索解空间,并在搜索的过程中用剪枝函数减去不可能产生最优解的子树,从而避免无效搜索。剪枝函数有两个,用 约束函数(constrain function) 可以在扩展结点处剪去不满足约束的子树;用 限界函数(bound function) 可以剪去得不到最优解的子树。

用回溯法解题的一个显著特征是在搜索过程中动态产生问题的解空间。在任何时刻,算法只保存从根结点到当前扩展结点的路径。如果解空间树中从根结点到叶结点的最长路径的长度为 h(n) ,则回溯法所需的计算空间通常为 O(h(n)) 。而显式地存储整个解空间则需要 O(2h(n)) (子集树) 或 O(h(n)!) (排列树) 的内存空间。

回溯法的伪代码如下:

// 子集树
void backtrack (int t) {
    if (t > n) return output(x); 
    for (int i = f(n,t); i <= g(n,t); i++) {
        x[t] = h(i);
        if (constraint(t) && bound(t))
            backtrack(t+1); 
    }
}

// 排列树
void backtrack (int t) {
    if (t > n) return output(x); 
    for (int i = f(n,t); i <= g(n,t); i++) {
        swap(x[t], x[i])
        if (constraint(t) && bound(t))
            backtrack(t+1); 
        swap(x[t), x[i])
    }
}

解空间

回溯法希望一个问题的解能够表示成一个 n 元向量 (x1,x2,...,xn) 的形式,这个向量叫做问题的 解向量。解向量的约束分成了显约束和隐约束。显约束 指的是分量 xi 的取值约束,比如 0-1 背包只能取 0 或者 1 两个值。隐约束 指的是为满足问题的解对不同的分量之间施加的约束。对于问题的一个实例,解向量满足显示约束条件的所有多元组,构成了该实例的一个 解空间

生成问题状态的基本方法

在回溯法搜索的过程中,有序树的结点根据状态可以分成下面三种:

  1. 扩展结点:一个正在产生儿子的结点称为扩展结点
  2. 活结点:一个自身已生成但其儿子还没有全部生成的节点称做活结点
  3. 死结点:一个所有儿子已经产生的结点称做死结点

生成问题状态有两种方法

深度优先的问题状态生成法

如果对一个扩展结点 R,一旦产生了它的一个儿子 C,就把 C 当做新的扩展结点。在完成对子树 C (以 C 为根的子树)的穷尽搜索之后,将 R 重新变成扩展结点,继续生成 R 的下一个儿子(如果存在)。

宽度优先的问题状态生成法

在一个扩展结点变成死结点之前,它一直是扩展结点。

为了避免生成那些不可能产生最佳解的问题状态(需要剪枝),要不断地利用限界函数来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量。具有限界函数的深度优先生成法称为回溯法。

回溯法效率分析

回溯法的效率在很大程度上依赖于一下的因素:

  • 产生 xk 的时间
  • 满足显约束的 xk 值的个数
  • 计算约束函数 constraint 的时间
  • 计算上界函数 bound 的时间
  • 满足约束函数和上界函数约束的所有 xk 的个数

好的约束函数能够显著地减少生成的节点的个数,但是这样的约束函数往往计算量较大,因此选择约束函数时,会在生成节点个数和约束函数计算量之间的折中。

举例

  • 0-1 背包问题(解空间:子集树)
  • 旅行售货员问题(解空间:排列树) TSP 即旅行商问题
  • 装载问题:把 n 个集装箱,装载到两个轮船中去;
  • 批处理作业调度
  • 符号三角形问题
  • n 后问题
  • 最大团问题
  • 图的 m 着色问题
  • 圆排列问题
  • 连续邮资问题

分支限界法

和回溯法的区别

  • 回溯法
    • 是找出解空间树中满足约束条件的所有解
    • 深度优先搜索
  • 分支限界法
    • 是找出解空间中满足约束条件的一个解,或者是某种意义下的最优解
    • 广度优先搜索或者是最小耗费(最大效益)

常见的两种分支限界法

队列式(FIFO)分支限界法

优先队列分支限界法

搜索策略

分支限界法的搜索策略:在拓展节点处,首先会生成其所有儿子节点(分支),然后再从当前的活节点表中选择下一个扩展节点。为了有效地选择下一扩展节点,以加速搜索进程,在每一个活节点处,计算一个函数值,并根据这些已计算出的函数值,在当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间树上最优解推进。

分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树和排列树。在搜索问题的解空间树时,分支限界法与回溯法对当前扩展结点所使用的扩展方式不同。

在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,那些导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被子加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所求的解或活结点表为空时为止。

举例

  • 0-1 背包问题(子集树)
  • 旅行商问题(用到了最小堆和优先队列,排列数)
  • 单源最短路径问题
  • 装载问题(队列式分支限界法;优先队列分支限界法 )
  • 布线问题
  • 最大团问题
  • 电路板排列问题
  • 批处理作业调度问题
    原文作者:分支限界法
    原文地址: https://blog.csdn.net/zhangxb35/article/details/53955231
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞