分支限界与回溯法对比

2024-01-11 11:18
文章标签 分支 回溯 对比 限界

本文主要是介绍分支限界与回溯法对比,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

分支限界法类似于回溯法,也是一种在问题的解空间树 T 上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出 T 中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。  
由于求解目标不同,导致分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。分支限界法的搜索策略是:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展对点。为了有效地选择下一扩展结点,以加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解。 
分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树和排列树。在搜索问题的解空间树时,分支限界法与回溯法对当前扩展结点所使用的扩展方式不同。在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,那些导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被子加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所求的解或活结点表为空时为止。
有一些问题其实无论用回溯法还是分支限界法都可以得到很好的解决,但是另外一些则不然。也许我们需要具体一些的分析——到底何时使用分支限界而何时使用回溯呢?下表列出了回溯法和分支限界法的一些区别:
方法
对解空间树的搜索方式
存储结点的常用数据结构
结点存储特性
常用应用
回溯法
深度优先搜索
堆栈
活结点的所有可行子结点被遍历后才被从栈中弹出
找出满足约束条件的所有解
分支限界法
广度优先或最小消耗优先搜索
队列、优先队列
每个结点只有一次成为活结点的机会
找出满足约束条件的一个解或特定意义下的最优解
A、一个比较适合采用回溯法解决的问题——n后问题
在n*n的国际象棋棋盘上摆下n个后,使所有的后都不能攻击到对方,找出所有符合要求的情况。
n后问题的解空间树是一棵排列树,一旦一种组合是一种解,则解与解之间不存在优劣的分别。直到搜索到叶节点时才能确定出一组解。这时我们用回溯法可以系统地搜索问题的全部解,而且由于解空间树是排列树的特性,代码的编写十分容易,在最坏的情况下,堆栈的深度不会超过n。如果我们采取分支限界法,在解空间树的第一层就会产生n个活结点,如果不考虑剪枝,将在第二层产生n*(n-1)个活节点,如此下去对队列空间的要求太高。n后问题不适合使用分支限界法处理的根源是它需要找处所有解的组合,而不是某种最优解(事实上也没有最优解可言)。形象一点来说,如果让我们自己人工解决n后问题,我们的思路应该是放一个后,看冲不冲突,不冲突的话放下一个,冲突的话改变位置,这样可以只用一个棋盘而且有规律地找到所有符合条件的情况,这正是回溯法的模拟过程。然而分支限界法则可以被这样表述。拿来一个棋盘,摆下一只后;再拿一个棋盘,再摆一只;待到每个棋盘都有一只后以后,每个棋盘又被分解成更多的盘面...,这样,棋盘越来越多,但是由于解和解(包括局部解)之间缺乏因果和限制的联系。棋盘之间并不能根据对方的信息获得什么,这显然是对资源的浪费。
B、一个既可以采用回溯法也可以采用分支限界法解决的问题——0-1背包问题
给定若干物品的重量和价值,以及一个背包的容量上限。求出一种方案使的背包中存放物品的价值最高。这个问题除了可以考虑回溯法和分支限界法之外,还可以用动态规划的方法解决,但在这里我们主要讨论前两种方法的对比。
0-1背包问题的解空间树是一棵子集树,所要求的解具有最优性质。
如果我们采用回溯法解决这个问题,我们采用如下的搜索策略:只要一个结点的左儿子结点是一个可行结点就搜索其左子树;而对于右子树,我们需要用贪心算法构造一个上界函数[[这个函数表明这个结点的子树所能达到的可能的最大容量(因为只有将0-1背包问题改变为背包问题才可能利用贪心算法,因此这个上界函数在绝大多数情况下不会是上确界函数)],只在这个上界函数的值超过当前最优解时才进入搜索。随着搜索进程的推进,最优解不断得到加强,对搜索的限制就越来越严格。
如果我们采用分支限界法解决这个问题,同样需要用到贪心算法构造的上界函数,所不同的是,这个上界函数的作用不在于判断是否进入一个结点的子树继续搜索,因为在搜索到达叶节点之前,我们也无法知道已经得到的最优解是什么。在这里,我们用一个最大堆来实现活结点的优先队列,上界函数的值将作为优先级,这样一旦有一个叶结点成为扩展结点,就表明已经找到了最优解。
可以看出,用两种方法处理0-1背包问题都有一定的可行性,相比之下回溯法的思路容易理解一些,但是这是一个寻找最优解的问题,由于采用了优先队列处理,不同的结点不再像n后问题那样没有相互之间的牵制和联系,用分支限界法处理效果一样很好。
C、一个比较适合采用分支限界法解决的问题——布线问题
印刷电路板将布线区域划分成n*m个方格阵列。精确的电路布线问题要求确定连接方格a的中点到方格b的中点的最短布线方案。在布线时,电路只能沿直线或直角布线。为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。
布线问题的解空间是一个图,适合采用队列式分支限界法来解决。从起始位置a开始将它作为第一个扩展结点。与该结点相邻并且可达的方格被加入到活结点队列中,并且将这些方格标记为1,表示它们到a的距离为1。接着从活结点队列中取出队首作为下一个扩展结点,并将与当前扩展结点相邻且未标记过的方格标记为2,并存入活节点队列。这个过程一直继续到算法搜索到目标方格b或活结点队列为空时为止(表示没有通路)。


现在来看上面两张图。左图演示了分支限界法的过程。最开始,队列中的活结点为标1的格子,随后经过一个轮次,活结点变为标2的格子,以此类推,一旦b方格成为活节点便表示找到了最优方案。为什么这条路径一定就是最短的呢?这是由于我们这个搜索过程的特点所决定的,假设存在一条由a至b的更短的路径,b结点一定会更早地被加入到活结点队列中并得到处理。 
右图表示了a和b之间的最短布线路径。值得一提的是,在搜索的过程中我们虽然可以知道结点距起点的路径长度,却无法直接获得具体的路径描述。为了构造出具体的路径,我们需要从目标方格开始向起始方格回溯,逐步构造出最优解,也就是每次向标记距离比当前方格距离少1的相邻方格移动,直至到达起始方格时为止。 
现在我们来考虑,为什么这个问题用回溯法来处理就是相当低效的。回溯法的搜索是依据深度优先的原则进行的,如果我们把上下左右四个方向规定一个固定的优先顺序去进行搜索,搜索会沿着某个路径一直进行下去直到碰壁才换到另一个子路径,但是我们最开始根本无法判断正确的路径方向是什么,这就造成了搜索的盲目和浪费。更为致命的是,即使我们搜索到了一条由a至b的路径,我们根本无法保证它就是所有路径中最短的,这要求我们必须把整个区域的所有路径逐一搜索后才能得到最优解。正因为如此,布线问题不适合用回溯法解决。
还有许多类似于布线的问题可以为分支限界法提供良好的展示空间。在今年10月底刚刚结束的ACM竞赛北京分赛区的比赛中,就出现一一个和布线问题十分相似的问题,可以用分支限界法取得很好的效果。

我们简单地描述一下这个问题。如左图,B1,B2,B3,B4代表一条蛇的身躯,其中B1是蛇头(蛇的身躯大小、起始位置以及障碍物的位置由输入获得),同样是在一个范围内搜索两个固定点蛇头和点(1,1)之间的最短路径。题目的要求相比于布线问题有一些简化的地方,比如终点的位置是固定的,而且只要求求出最短路径的长度,不要求写出具体的路径,也就是可以省出回溯起点的步骤,但是它也有一个需要做一定处理才能解决的推广,就是蛇所要移动的方向上除了不能是障碍物之外,还不能是自己身躯的一部分,比如蛇从左图的位置移动,不能向上或向右移动而只能移动到下方B1的位置形成右图的局面。
要做如何的处理才能解决这个问题呢?可以采取布线问题的解决框架,但是在每个结点处所出现的障碍物需要变化。如果我们仅仅在每个结点处把蛇身标记为与障碍物相同的记号固然是一种容易想到的思路,但是这样处理不利于从一个格子的障碍物情形推广到相邻格子的障碍物情形。比较有效的做法是将蛇身描述成一个定长队列,队列首是蛇尾而队列尾是蛇头(如果为了思路的连贯性,也可以反过来处理,但是这样应该使用双向队列),这样一来从一个方格推广到相邻方格的时候,我们只要对这个队列做一步处理就可以得到新的描述蛇身的队列。然后,可以用这个队列和方格本身的位置组成一个结构或类,在求解过程中依然使用队列式分支限界法,只不过队列中的元素是我们所定义的那个结构或类的对象。利用STL等一些泛型技术,这个过程还是比较容易实现的。
struct Position //描述一个位置的结构
{

int posx,posy
 
};
struct SnakeNode //描述分支限界法队列中一个结点的结构
{

Position square
;//方格位置信息
list<Position> snakeBody(snakeLength);
//描述蛇身的队列,每一个格的蛇仍用Position描述
}

这样初始时我们用queue<SnakeNode> nodeList(0)建立这个队列,之后的过程和布线问题就基本一致了。

原文地址:http://blog.163.com/d_cjiang/blog/static/11866648220100810948486/

这篇关于分支限界与回溯法对比的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/594194

相关文章

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

回溯——9.全排列

力扣题目链接 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3]输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 解题思路 问题建模:题目要求给出一个数组的所有排列组合,属于典型的全排列问题,这可以用回溯法来解决。回溯法通过递归的方式,依次将数组中的每个元素放入排列中,直到生成

类的load方法和initialize方法对比

1. load方法在main()之前被调用,而initialize方法在main()之后调用 load方法实际是在load_images过程中被调用的。load_images会将当前应用依赖的所有镜像(动态库)加载到内存,在在加载中首先是对镜像进行扫描,将所有包含 load 方法的类加入列表 loadable_classes ,然后从这个列表中逐一调用其所包含的 load 方法。 +[XXCl

JavaScript正则表达式六大利器:`test`、`exec`、`match`、`matchAll`、`search`与`replace`详解及对比

在JavaScript中,正则表达式(Regular Expression)是一种用于文本搜索、替换、匹配和验证的强大工具。本文将深入解析与正则表达式相关的几个主要执行方法:test、exec、match、matchAll、search和replace,并对它们进行对比,帮助开发者更好地理解这些方法的使用场景和差异。 正则表达式基础 在深入解析方法之前,先简要回顾一下正则表达式的基础知识。正则

【HarmonyOS】-TaskPool和Worker的对比实践

ArkTS提供了TaskPool与Worker两种多线程并发方案,下面我们将从其工作原理、使用效果对比两种方案的差异,进而选择适用于ArkTS图片编辑场景的并发方案。 TaskPool与Worker工作原理 TaskPool与Worker两种多线程并发能力均是基于 Actor并发模型实现的。Worker主、子线程通过收发消息进行通信;TaskPool基于Worker做了更多场景化的功能封装,例

一些数学经验总结——关于将原一元二次函数增加一些限制条件后最优结果的对比(主要针对公平关切相关的建模)

1.没有分段的情况 原函数为一元二次凹函数(开口向下),如下: 因为要使得其存在正解,必须满足,那么。 上述函数的最优结果为:,。 对应的mathematica代码如下: Clear["Global`*"]f0[x_, a_, b_, c_, d_] := (a*x - b)*(d - c*x);(*(b c+a d)/(2 a c)*)Maximize[{f0[x, a, b,

claude和chatgpt对比:哪一个更适合你?

前言 我们都知道,Claude和ChatGPT都是当前人工智能领域中备受关注的对话生成模型,作为国外AI模型两大巨头,好像他们的实力都不相上下呀! 这时就会有很多同学疑惑,那我如果想选择AI,到底是选择Claude,还是ChatGPT呢?哪个更好呢?他们之间有什么不同独特的地方呢?他们又分别适合在哪些场景使用呢? 技术背景 Claude是由Anthropic公司开发的高性能模型,而Chat

风控系统之指标回溯,历史数据重跑

个人博客:无奈何杨(wnhyang) 个人语雀:wnhyang 共享语雀:在线知识共享 Github:wnhyang - Overview 回顾 默认你已经看过之前那篇风控系统指标计算/特征提取分析与实现01,Redis、Zset、模版方法。 其中已经介绍了如何利用redis的zset结构完成指标计算,为了方便这篇文章的介绍,还是在正式开始本篇之前回顾一下。 时间窗口 zset

算法复杂度 —— 数据结构前言、算法效率、时间复杂度、空间复杂度、常见复杂度对比、复杂度算法题(旋转数组)

目录 一、数据结构前言 1、数据结构 2、算法 3、学习方法 二、 算法效率 引入概念:算法复杂度  三、时间复杂度 1、大O的渐进表示法 2、时间复杂度计算示例  四、空间复杂度 计算示例:空间复杂度 五、常见复杂度对比 六、复杂度算法题(旋转数组) 1、思路1 2、思路2 3、思路3 一、数据结构前言 1、数据结构         数据结构(D

Matplotlib图像读取和输出及jpg、png格式对比,及透明通道alpha设置

图像像素值 图像像素值一般size为3,也就是通道数,分别代表R,G,B,如果只有单一 一个值则表示灰度值,也就是说一张二维图片,当长和宽都为1080时,那么若是灰度图像,图像尺寸为(1080,1080,1)若是RGB图像则为(1080,1080,3), jpg、png图像格式 jpg图像的灰度值范围和RGB范围为[0,255],数值类型为uint8,也就是无符号整数 png图像的灰度值范