在开始写关于集束搜索的文章之前,我发现我对很多相关的算法都不是很熟悉,这严重影响到了我对集束搜索的理解,为了能让自己更好的理解集束搜索,我又回顾了一些基础。
我的回顾之旅:
BFS算法中,我总结了两篇二叉树的BFS搜索和无向图的BFS搜索,在理解BFS搜索算法的过程中又额外涉及到了通过树的中序和先序遍历生成二叉树和存储无向图的邻接矩阵和邻接链表这两个知识点,有兴趣的朋友可以查看我的文集“算法之旅”。
好了,闲话不多说,我们先开始说一说什么是集束搜索吧。
Beam Search
尽管广度优先搜索BFS能够保证在无权无向图中找到起点与终点之间的最短路径,但是由于该算法对存储的消耗是指数级别的,因此对于搜索空间较大的问题,广度优先搜索算法几乎无用武之地,因为往往在找到最优解之前,存储内存就已经消耗完了,但是如果我们不得不去在一个搜素空间较大的图中寻找两点之间的路径时,我们该怎么办呢?前人给了我们这样一个解决方法:那就是放宽对路径的要求,不再要求路径一定是最短路径,退而求其次,寻找一个相对较短的路径,这个相对值是可以在我们能接受的范围之内的,这种解决方案就是集束搜索的算法思想了。
集束搜索Beam Search借助了一个启发式代价函数henuristic cost function,h用来预测当前节点到目标节点的代价的大小,这个启发式代价函数我们不是第一次接触了,在上一篇文章A*搜索算法中我们已经详细介绍了,除此之外,集束搜索还是用了一个束宽度beam width,B来限制在每一层的遍历时存储该层的节点数量。
在BFS算法中,我们是将每一层的所有节点都存储到队列queue中,而集束搜索是根据h的值,只选择B个节点存储到队列中,这样的话我们就能保证在得到结果之前将存储耗尽的情况了。
现在我们来分析一下集束搜索:
通过考虑四个特征来分析图搜索算法通常是有效的:
完整性:如果搜索算法在解决方案存在时找到解决方案(目标节点),则搜索算法已完成。
最优性:如果找到最优解,搜索算法是最优的。在波束搜索算法的情况下,这意味着算法必须找到从起始节点到目标节点的最短路径。
时间复杂度:这是算法速度的数量级估计。通过分析算法执行期间生成的节点数来确定时间复杂度。
空间复杂度:这是算法的内存消耗的数量级估计。空间复杂度由算法执行期间任何时候必须存储的最大节点数决定。
完整性
通常,光束搜索算法不完整。这在上面的跟踪1中说明。即使内存没有耗尽,算法也无法找到目标,因为它无法向BEAM添加任何节点。因此,即使给定无限的时间和存储器,当存在从起始节点到目标节点的路径时,波束搜索算法也可能错过目标节点。更准确的启发式功能和更大的波束宽度可以提高Beam Search找到目标的机会。然而,这种完整性的缺乏是光束搜索算法的最重要的弱点之一。
最优
正如光束搜索算法不完整一样,它也不能保证是最佳的。这由上面的跟踪2显示。在此示例中,Beam Search找到了目标节点,但未能找到目标的最佳路径,即使图1中的启发式是可接受的(低估了每个节点的目标成本)和一致性(低估了相邻节点之间的成本) )。这是因为波束宽度和不准确的启发函数导致算法错过扩展最短路径。更精确的启发式功能和更大的波束宽度可以使Beam Search更有可能找到达到目标的最佳路径。
时间复杂性
光束搜索算法完成的时间往往取决于启发式函数的准确性。不准确的启发式函数通常会强制算法扩展更多节点以找到目标,甚至可能导致它无法找到目标。在最坏的情况下,启发式函数将Beam Search一直引导到搜索树中最深的层次。因此,最坏情况时间是 O(Bm),其中B是波束宽度,m是搜索树中任何路径的最大深度。这种时间复杂度是线性的,因为波束搜索算法仅扩展B每个级别的节点; 它不会像许多具有指数时间复杂性的搜索算法那样在每个级别上进行更广泛的分支。该算法执行的速度是其最大优势之一。
空间复杂性
Beam Search的内存消耗是其最理想的特性。因为该算法仅在搜索树中的每个级别存储B个节点,所以最坏情况的空间复杂度为 O(Bm),其中B是波束宽度,m是搜索树中任何路径的最大深度。这种线性内存消耗允许Beam Search深入探测大型搜索空间,并可能找到其他算法无法达到的解决方案。
集束搜索算法的伪码:
/ *初始化* /
g = 0;
hash_table = {start};
BEAM = {start};
/ *主循环* /
while(BEAM≠∅){//循环,直到BEAM不包含节点
SET =∅; //空集
/ *生成SET节点* /
for(each state in BEAM){
for(each successor of state){
if(successor == goal)返回g + 1;
SET =SET∪{successor}; //添加SET的后继者
}
}
BEAM =∅; //空集
g = g + 1;
/ *为下一个循环填充BEAM * /
while((SET≠∅)AND(B> | BEAM |)){// set不为空且BEAM中的节点数小于B
state =具有最小h值的SET中的后继者;
SET = SET \ {state}; //从SET中删除状态
if(state∉hash_table){//状态不在hash_table中
if(hash_table为full)返回∞;
hash_table = hash_table∪{state}; //将状态添加到hash_table
BEAM = BEAM∪{state}; //将状态添加到BEAM
}
}
}
//未找到目标,BEAM为空 - Beam Search未能找到目标
返回∞;
网友评论