写在前面:两种算法很相似,都是对于求解空间树T上检索问题的算法。在通常情况下,分支限界法和回溯法求解目标是不同的。回溯法求得的是T中满足约束条件的所有解,而分支限界法的求解目标是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极值的解(最优解)
分支限界算法
本质上以广度优先遍历搜索空间树T,只关心使给定函数得到最优解。若算法找到一个代价为n的解,当有一个部分解,它的代价 > n,那么不会对该部分解进行扩展进一步探索其代价
基本思路
每一个活跃的节点都只有一次机会称为扩展节点,一旦活跃节点成为扩展节点,就会一次性产生所有的儿子节点,然后在这些儿子节点中进行筛选,把那些代价大于n的儿子节点删除,剩下的儿子节点作为活跃节点存在。不断重复这个过程,直到找到最优解或不存在任意一个活跃节点为止
剪枝策略
对某个节点进行搜索时,会先估算出目标的解(预估值),再确定是否继续向下搜索(选择最小损耗的结点进行检索)
常用方法
队列式(FIFO)分支限界法:使用普通的队列,按照广度优先遍历的次序从队列中拿出先放入队列中的结点
优先队列(PQ)分支限界法:使用覆写比较运算的堆或者按优先级排序的队列,并不是先进的一定先出,是最佳优先
回溯算法
本质上是决策树深度优先遍历问题,用的是穷举思路,但实现过程中通过添加剪枝策略可以达到避免无效搜索的目的
基本思路
每一个节点只有在依次遍历完所有儿子节点后(穷举),才从扩展节点变成非活跃节点。要点:路径(已经做出的选择),选择列表(当前可以做出的选择),结束条件(达到决策树底层,无法再做选择)
剪枝策略
- 约束函数:在能产生子选择的结点(没有产生完)处剪去不满足约束条件的无效选择子树
- 显式约束:对选择进行直接施加约束
- 隐式约束:通过其它因素的互相作用来间接对选择施加约束
- 限界函数:减去得不到最优解的子树(需要提前预判或使用预估值)
回溯框架
递归
# N皇后问题
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
迭代
def backtrack():
result = []
if 不满足结束条件:
for 选择 in 选择列表:
if 满足约束条件或限界条件:
result.add(路径)
else:
继续向下检索
else:
触底,返回上一层重新选择
子集树
# 0-1背包问题【一个N数的集合里有多少符合条件的子集 (模式挖掘)】
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
else:
for 选择 in 选择列表:
if 满足约束条件或限界条件:
backtrack(路径, 选择列表) # 进入下一层
撤销选择
排列树
# 旅行售货员问题
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
else:
for 选择 in 选择列表:
做选择
if 满足约束条件或限界条件:
backtrack(路径, 选择列表) # 进入下一层
做选择
HUIM中的运用
解空间
-
对于数据集中的所有交易项的子集,用树型结构的思路进行挖掘
-
对于项集的所有子集,不同的算法采用不同的子集树结构
约束条件
HUI-Miner
思路:使用回溯算法,深度遍历直到探底,对各个节点采用分支限界函数,筛选掉不合格的节点,避免无效检索
剪枝策略:使用上边界 ,其中
EFIM
思路:与HUI-Miner同样
剪枝策略:,其中
FHM
思路:以每个一元项节点为根,再使用回溯算法,进行深度遍历
剪枝策略:
参考
- labuladong:回溯算法套路详解
- 高大宽333:回溯算法的递归和迭代
- 晓强DL:分支限界法
- 于星:回溯法、分支限界法