算法的乐趣之枚举法

发布 : 2019-03-24 浏览 :

穷举法

穷举法又称穷举搜索法,是一种在问题域的解空间中对所有可能的解穷举搜索,并根据条件选择最优解的方法的总称。

使用穷举法解决问题,基本上就是以下两个步骤:

  • 确定问题的解(或状态)的定义、解空间的范围以及正确解的判定条件;
  • 根据解空间的特点来选择搜索策略,逐个检验解空间中的候选解是否正确;

解空间的定义

解空间就是全部可能的候选解的一个约束范围,确定问题的解就在这个约束范围内,将搜索策略应用到这个约束范围就可以找到问题的解。要确定解空间,首先要定义问题的解并建立解的数据模型。如果解的数据模型选择错误或不合适,则会导致解空间结构繁杂、范围难以界定,甚至无法设计穷举算法。

很多问题在设计穷举法时都不是直接根据问题的答案设计解空间的数据模型,因为那样会造成穷举算法设计困难,甚至无法实现算法。如果将问题的解扩展为一组状态,通过状态可以简单推出问题的解,并且状态可以通过演变成另一个状态,将解空间转化成一个可以遍历的状态空间,就可以将对问题的解的穷举遍历变成对这个状态空间的的穷举遍历,从而简化算法设计的难度。

穷举解空间的策略

穷举解空间的策略就是搜索算法的设计策略,根据问题的类型,解空间的结构可能是线性表、集合、树或者图,对于不同类型的解空间,需要设计与之相适应的穷举搜索算法。简单的问题可以用通用的搜索算法,比如线性搜索算法用于对线性解空间的搜索,广度优先和深度优先的递归搜索算法适用于树型解空间或更复杂的图型解空间。

一般来说,为了加快算法的求解,通常会在搜索算法的执行过程中辅助一些剪枝算法,排除一些明显不可能是正确解的检验过程,来提高穷举的效率。剪枝一个很形象的比喻,如果某一个状态节点确定不可能演化出结果,就应该停止从这个状态节点开始的搜索,相当于状态树上这一分枝就被剪掉了。除了采用剪枝策略,还可以使用限制搜索深度的方法加快算法的收敛,但是限制搜索深度会导致无解,或错过最优解,通常只在特定的情况下使用,比如博弈树的搜索。

盲目搜索和启发式搜索

对于线性问题的盲目搜索,就是把线性表中的所有算法按照一定的顺序遍历一遍,对于复杂问题的盲目搜索,常用广度优先搜索和深度优先搜索这两种盲目搜索算法。

启发性搜索需要一些额外信息和操作来“启发”搜索算法,根据这些信息的不同,启发的方式也不同。比如,如果知道解空间的状态分布呈现正态分布的特征,则可以从分布中间值开始向两边搜索,因为在中间值附近出现最优解的概率更高,这就是启发式搜索。

剪枝策略

对解空间穷举搜索时,如果有一些状态节点可以根据问题提供的信息明确地被判定为不可能演化出最优解,也就是说,从此节点开始遍历得到的子树,可能存在正确的解,但是肯定不是最优解,就可以跳过此状态节点的遍历,这将极大地提高算法的执行效率,这就是剪枝策略,应用剪枝策略的难点在于如何找到一个评价方法(估值函数)对状态节点进行评估。

除了最优解问题,还有一种情况也会用到剪枝策略。对解空间内的状态节点遍历搜索的过程中,会有一些在特定搜索策略下重复出现的状态节点,对这些状态节点如果不做特殊处理,不仅会因为重复处理相同的状态节点而降低效率,还可能会导致深度优先搜索算法“陷入”到某个子树的搜索中无法退出。举个例子,如果出现对状态 A 搜索得到子状态 B,对状态 B 搜索得到子状态 C,对状态 C 搜索又可得到子状态 A 的情况,就会使得搜索算法陷入“死循环”。在这种情况下,常用的剪枝策略就是找到一种算法对状态计算校验值,通过比较校验值判断是否是已经处理过的状态节点。

剪枝和启发

剪枝不是启发性搜索。剪枝的原理是在结果已经搜索出来或部分搜索出来(比如树的根节点已经搜索出来了,但是叶子节点还没有搜索出来)的情况下,根据最优解的判断条件,确定这个方向上不可能存在最优解,从而放弃对这个方向的继续搜索。而启发性搜索通常是根据启发函数给出的评估值,在结果出来之前就朝着最可能出现最优解的方向搜索。它们的差异点在于是根据结果进行判断还是根据启发函数的评估值进行判断。

搜索算法的评估和收敛

穷举法虽然被称为灵活的“通用算法”,但也不是万能的,穷举法最大的敌人是问题的规模。很多问题,当规模大到一定程度时,使用穷举法就只具有理论上的可行性。对某些问题,穷举法是最后的办法,但是问题规模又大到无法对解空间进行完整搜索,这时候就需要对搜索算法进行评估,并确定一些收敛原则。收敛原则是只要能找到一个比较好的解就返回(不求最好),根据解的评估判断是否需要继续下一次搜索。

本文作者 : HeoLis
原文链接 : http://ishero.net/算法的乐趣之枚举法.html
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

学习、记录、分享、获得

微信扫一扫, 向我投食

微信扫一扫, 向我投食