问题的解空间
一个复杂问题的解决方案是由若干个小的决策步骤组成的决策序列,解决一个问题的所有可能的决策序列构成该问题的解空间。
- 解空间中满足约束条件的决策序列称为可行解
- 在约束条件下使目标达到最优的可行解称为该问题的最优解
- 问题的解由一个不等长或等长的解向量X={x1,x2,…,xn}组成,其中分量xi表示第i步的操作
- 所有满足约束条件的解向量组构成了问题的解空间
- 解空间一般用树形式组织,也称为解空间树或状态空间
求解问题类型
- 找所有解
- 找最优解
什么是回溯法
在解空间树中,按照深度优先搜索的策略,从更解点开始出发搜索解空间树。
- 根结点是活结点也是当前的扩展节点
1)活结点:自身已生成但其孩子解点没有全部生成但结点
2)扩展节点:正在产生孩子结点的结点 - 当从状态si搜索到状态si+1后,如果si+1变为死结点,则从状态si+1回退到si,再从si找其他可能的路径
- 若用回溯法求问题的所有解时,需要回溯到根结点,且根结点的所有可行的子树都要已被搜索完才结束。若用回溯法求任一个解时,只要搜索到问题的一个解就可以结束
两种策略
回溯法搜索解空间时,通常采用两种策略避免无效搜索,提高回溯的搜索效率:
- 用约束函数在扩展结点处剪除不满足约束的子树
- 用限界函数剪去得不到问题解或最优解的子树。
这两类函数统称为剪枝函数
回溯法的一般步骤
- 针对所给问题,定义问题的解空间;
- 确定结点的扩展规则。
- 以深度优先方式搜索解空间树,并在搜索过程中可以采用剪枝函数来避免无效搜索。
状态空间树的产生方法
- 深度优先的问题状态生成法:对一个扩展结点R,一旦产生了它的一个儿子C,就把C当做新的扩展结点。在完成对子树C(以C为根的子树)的穷尽搜索之后,将R重新变成扩展结点,继续生成R的下一个儿子(如果存在)
- 回溯法:为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(bounding function)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量
🌟回溯法=深度优先搜索+剪枝
回溯法的基本思想
- 适用:求解搜索问题和优化问题.
- 搜索空间:树,结点对应部分解向量,可行解在树叶上.
- 搜索过程:采用系统的方法隐含遍历搜索树.
- 搜索策略:深度优先,广度优先,函数优先,宽深结合等.
- 结点分支判定条件:满足约束条件—分支扩张解向量;不满足约束条件—回溯到该结点的父结点.
- 动态生成
- 扩展结点(一个正在产生儿子的结点称为扩展结点)
- 活结点(正在访问该结点为根的子树)
- 死结点(该结点为根的子树遍历完成)
7.存储:当前路径
回溯法的适用条件
多米诺性质
定义:
设X=<x1,x2,…, xn> 是问题的解,Xi=<x1,x2,…,xi>, Xi+1=<x1,x2,…,xi,xi+1>,Xi,Xi+1⊆X ,Xi和Xi+1分别是搜索到第i层和第i+1层的解.如果P(Xi+1)→P(Xi) , 即 P(Xi+1) 蕴含 P(Xi),则称该问题满足多米诺性质.
多米诺性质为回溯法的剪枝提供了数学依据。
两种典型的解空间树
子集树(subset tree):当所给的问题是从n个元素的集合s中找出满足某种性质的子集时,相应的解空间称为子集树。O(n^2)
排列树(permutation tree):当所给的问题时确定n个元素满足某种性质的排列时,相应的解空间称为排列树。O(n!)
子集树的效率往往大于排列树,选择自己树或排列树需要依据题目要求来确定。
回溯法的算法框架
解空间为子集树或N叉树
int x[n]; //x存放解向量,全局变量
void backtrack(int i) //求解子集树的递归框架
{ if(i>n) //搜索到叶子结点,输出一个可行解
输出结果;
else
{ for (j=下界;j<=上界;j++) //用j枚举i所有可能的路径
{ x[i]=j; //产生一个可能的解分量
… //其他操作
if (constraint(i) && bound(i))
backtrack(i+1); //满足约束条件和限界函数,继续下一层
}
}
}
解空间为排列树
int x[n]; //x存放解向量,并初始化
void backtrack(int i) //求解排列树的递归框架
{ if(i>n) //搜索到叶子结点,输出一个可行解
输出结果;
else
{ for (j=i;j<=n;j++) //用j枚举i所有可能的路径
{ … //第i层的结点选择x[j]的操作
swap(x[i],x[j]); //为保证排列中每个元素不同,通过交换来实现
if (constraint(i) && bound(i))
backtrack(i+1); //满足约束条件和限界函数,进入下一层
swap(x[i],x[j]); //恢复状态
… //第i层的结点选择x[j]的恢复操作
}
}
}
回溯法效率分析
回溯算法在很大程度上依赖于一下因素:
回溯算法的效率在很大程度上依赖于以下因素:
(1)产生下一个x[k]的时间;
(2)满足显约束条件的x[k]值的个数;
(3)计算约束函数Bi的时间;
(4)满足约束函数Bi的所有x[k]的个数。
因此,在选择约束函数时通常存在生成结点数与约束函数计算量之间的折中。
回溯法与深度优先遍历的异同
- 相同点:回溯法在实现上也是遵循深度优先的,即一步一步往前探索,而不像广度优先遍历那样,由近及远一片一片地搜索。
- 不同点:
- 访问顺序不一样:深度为遍历,本质为无须。回溯时求解过程,本质是有序的
- 访问次数的不同:深度对访问过的结点不再访问。回溯中已访问过的顶点可能再次访问。
- 剪枝的不同:深度优先遍历不含剪枝,而很多回溯算法采用剪枝条件剪除不必要的分支以提高效率。