[算法分析]回溯法


回溯法:以深度优先方式系统搜索问题的解

  • 在问题的解空间树中,按深度优先策略,从根节点出发搜索解空间树

    • 当搜索到解空间树的任一结点时,判断该结点是否包含问题的解
      • 如果确定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯;(剪枝)
      • 否则,进入该子树,继续深度优先搜索;
  • 求解问题的性质

    • 求问题的所有解时,要回溯到根,且根节点的所有子树都被搜索后才结束
    • 求问题的一个解时,只要搜索到问题的一个解即可

回溯法的算法框架

问题的解空间

  • 定义:问题的解空间至少应包含问题的一个最优解
  • 组织:通常组织为树或图的形式——有利于回溯法对整个解空间的搜索

回溯法的基本思想

  1. 确定解空间的组织结构
  2. 从开始结点(根结点)出发,深度优先搜索整个解空间。(这个开始结点成为活结点,也为当前的扩展结点)
  3. 在当前扩展结点处,向纵深方向搜索一个新结点。(这个新结点成为活结点,也为当前的扩展结点)
    • 如果不能向纵深方向搜索了,则当前结点成为死结点。并且往回移动(回溯)到最近的活结点处
  4. 回溯法按上述方式递归地在解空间中搜索,直到找到所要求的解解空间中无活结点为止

如何避免回溯法的无效搜索

剪枝函数:

  • 约束函数 在扩展结点处剪去不满足约束的子树——0-1背包问题
  • 上界函数 剪去得不到最优解的子树——TSP问题

递归回溯

void backtrack(int t)   // 递归深度t
{
    if(t > n)   // 最大深度n
        output(x);
    else
        for(int i = f(n, t); i <= g(n, t); ++i)
        {   // 在当前扩展结点处未搜索过的子树
            x[t] = h(i);
            if(constrain(t) && bound(t))   // 约束函数 和 上界函数
                backtrack(t+1);
        }
}

迭代回溯

void iterativeBacktrack()
{   // 采用树的非递归深度优先遍历(树的遍历)
    stack<T> s;   // 存结点的堆栈
    T p = t;   // 当前扩展结点p,根结点t
	while(!s.empty())
    {
        for(int i = 0; i < p.child.size(); ++i)
        {   // 在当前扩展结点处未搜索过的子树
            d = p.child[i];   // 子树结点d
            if(constrain(d) && bound(d))   // 约束函数 和 上界函数
                s.push(d);
        }
        p = s.top();
        s.pop();
    }
}

子集树和排列树

  • 子集树

    当所给问题是从n个元素的集合S中找出S满足某种性质的子集时(选或不选某个结点),相应的解空间树被称为子集树。

    如0-1背包问题,这类问题通常有2n个叶结点,其结点总数为2n+1-1。

    遍历子集树需要Ω(2n)计算时间

    image-20201118204948206


  • 排列树

    当所给问题是确定n个元素满足某种性质的排列时,相应的解空间树被称为排列树。

    如旅行商问题TSP,这类问题通常有n! 个叶结点

    遍历排列树需要Ω(n!)计算时间



实例分析

旅行商问题 (排列树)

  • 问题描述:

某销售商要到若干个城市去推销商品,已知各城市之间的路程(或费用)。要求为给旅行商选择一条从驻地出发的路径,经过每个城市一次,最后返回驻地,使得该路径(或总的旅费)最短(或最小)。

  • 问题分析:

NP难问题。设𝐺=(𝑉,𝐸)是一个带权图。图中各边的权为正数。图的一条周游路线是包括𝑉中的每个顶点在内的一条回路。周游路线的费用是这条路线上所有边的权之和。——旅行商问题就是要在图𝐺中找出费用最小的周游路线。

  • 实例分析:

对于n = 4的TSP问题,可能周游的路线有6条。

对于n = N的TSP问题,可能周游路线有(N-1)!条


  • 回溯法求解过程:

  • 剪枝函数的引入:

采用当前已知的最优解(费用X)为标准,若当前路径长度 ≥ X,则表明以此结点为根结点的子树中不包含最优解,将子树中所有结点都设置为死结点,并向当前结点的最近祖先结点回溯。


  • 时间复杂度及算法:

O(n!)

常用TSP解决方案:遗传算法;模拟退火;神经网络;并行算法


骑士巡游问题(子集树)

符号三角形问题(子集树)

N皇后问题(子集树)

最大团问题(子集树)

图的m着色问题(子集树)

圆排列问题(排列树)

电路板排列问题(排列树)

连续邮资问题



回溯法效率分析

影响因素
  1. 产生 状态点x[k] 的时间
  2. 满足显约束的 状态点x[k] 的个数
  3. 计算约束函数constrain的时间
  4. 计算上界函数bound的时间
  5. 满足约束函数和上界函数的所有结点个数

解空间的结构一旦确定,前3个因素就可以确定,剩下的就是考虑回溯过程中生成的结点个数

重排原理

在搜索试探时,选取x[i]的值顺序是任意的。在其他条件相当的前期下,让可取值最少的x[i]优先有效。这样,在低层剪枝时,可以剪去更多的子树,可提高回溯法效率。

概率方法估计将产生的结点数

原因:

对两个非常相近的实例,其产生的结点数也会存在很大的差别


方案:

在解空间树上产生一条随机路径,然后沿该路径估算解空间中满足约束条件的结点数m

多选取几条不同的路径,分别计算m,然后平均,估算更准确



小结

  1. 回溯法的基本思想

  2. 问题解空间的定义和组织

  3. 剪枝函数的设计

  4. 掌握利用回溯法求解问题的方法

  5. 效率分析

     

    PS: 本节内容偏于设计,于《算法导论》中无本章节,用于补充


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM