【算法】分支限界

article/2025/6/29 9:41:12

一、基本思想

(分支限界, 分枝限界, 分支界限 文献不同说法但都是一样的)
分支限界法类似于回溯法,也是一种在问题的解空间树上搜索问题解的算法。

但一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出解空间树中满足约束条件的所有解(或一个解),而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

分支限界法与回溯法对解空间的搜索方式不同

  1. 回溯法按深度优先策略搜索解空间;
  2. 分支限界法以广度优先或以最小耗费优先的方式搜索解空间。

分支限界法的搜索策略是广度优先或以最小耗费优先的方式搜索:

  1. 在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展结点。
  2. 为了有效地选择下一个扩展结点,加速搜索的进程,在每一个活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。

搜索解空间时,分支限界法与回溯法的主要区别在于它们对当前扩展结点所采用的扩展方式不同。在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。

分支限界法与回溯法的主要区别

方法解空间搜索方式存储结点的数据结构结点存储特性常用应用
回溯法深度优先活结点的所有可行子结点被遍历后才从栈中出栈找出满足条件的所有解
分支限界法广度优先队列,优先 队列每个结点只有一次 成为活结点的机会找出满足条件一个解或 者特定意义的最优解

二、分支限界法的设计思想

  1. 设计合适的限界函数
  2. 组织活结点表
  3. 确定最优解的解向量

2.1 设计合适的限界函数

在搜索解空间树时,每个活结点可能有很多孩子结点,其中有些孩子结点搜索下去是不可能产生问题解或最优解的。
可以设计好的限界函数在扩展时删除这些不必要的孩子结点,从而提高搜索效率

假设活结点si有4个孩子结点,而满足限界函数的孩子结点只有2个,可以删除这2个不满足限界函数的孩子结点,使得从si出发的搜索效率提高一倍。
在这里插入图片描述
目标函数是求最大值:则设计上界限界函数ub(根结点的ub值通常大于或等于最优解的ub值),若si是sj的双亲结点,应满足ub(si)≥ub(sj),当找到一个可行解ub(sk)后,将所有小于ub(sk)的结点剪枝。
目标函数是求最小值:则设计下界限界函数lb(根结点的lb值一定要小于或等于最优解的lb值),若si是sj的双亲结点,应满足lb(si)≤lb(sj),当找到一个可行解lb(sk)后,将所有大于lb(sk)的结点剪枝。

2.2 组织活结点表

根据选择下一个扩展结点的方式来组织活结点表,不同的活结点表对应不同的分支搜索方式。
队列式分支限界法
队列式分支限界法
优先队列式分支限界法

2.2.1 队列式分支限界法

队列式分支限界法将活结点表组织成一个队列,并按照队列先进先出(FIFO)原则选取下一个结点为扩展结点。步骤如下:

  1. 将根结点加入活结点队列。
  2. 从活结点队中取出队头结点,作为当前扩展结点。
  3. 对当前扩展结点,先从左到右地产生它的所有孩子结点,用约束条件检查,把所有满足约束条件的孩子结点加入活结点队列。
  4. 重复步骤②和③,直到找到一个解或活结点队列为空为止。

2.2.2 优先队列式分支限界法

优先队列式分支限界法的主要特点是将活结点表组组成一个优先队列,并选取优先级最高的活结点成为当前扩展结点。步骤如下:

  1. 计算起始结点(根结点)的优先级并加入优先队列(与特定问题相关的信息的函数值决定优先级)。
  2. 从优先队列中取出优先级最高的结点作为当前扩展结点,使搜索朝着解空间树上可能有最优解的分支推进,以便尽快地找出一个最优解。
  3. 对当前扩展结点,先从左到右地产生它的所有孩子结点,然后用约束条件检查,对所有满足约束条件的孩子结点计算优先级并加入优先队列。
  4. 重复步骤②和③,直到找到一个解或优先队列为空为止。

2.2.3 堆、最大堆、最小堆

堆的定义如下:
(1)堆是一棵完全二叉树;
(2)堆中某个节点的值总是不大于或不小于其孩子节点的值;
(3)堆中每个节点的子树都是堆。

当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。
当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。

(下图为最大堆)
在这里插入图片描述
(下图为最小堆)
在这里插入图片描述
在算法实现时通常用最大堆来实现最大优先队列;用最小堆来实现最小优先队列

2.3 确定最优解的解向量

分支限界法在搜索解空间树时,结点的处理是跳跃式的,回溯也不是单纯地沿着双亲结点一层一层地向上回溯,因此当搜索到某个叶子结点且该结点对应一个可行解时,如何得到对应的解向量呢?

  1. 对每个扩展结点保存从根结点到该结点的路径。
    每个结点带有一个可能的解向量。这种做法比较浪费空间,但实现起来简单。
  2. 在搜索过程中构建搜索经过的树结构。
    每个结点带有一个双亲结点指针,当找到最优解时,通过双亲指针找到对应的最优解向量。这种做法需保存搜索经过的树结构,每个结点增加一个指向双亲结点的指针。

三、案例

3.1 装载问题

**【问题描述】**有一批总重为W的共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且 W ≤c1+c2。
要求确定是否有一个合理的装载方案可将这些集装箱装上这2艘轮船。如果
有,找出一种装载方案。

其实质是要求将第一艘轮船尽可能装满(最优装载)。即选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近。

采用队列式分支限界法求解

首先检测当前扩展结点的左儿子结点是否为可行结点。如果是则将其加入到活结点队列中。然后将其右儿子结点加入到活结点队列中(右儿子结点一定是可行结点)。2个儿子结点都产生后,当前扩展结点被舍弃。

节点的左子树表示将此集装箱装上船,
右子树表示不将此集装箱装上船。

当队列元素的值为-1时,表示队列已到达解空间树同一层结点的尾部。
当取出的元素是-1时,要判断当前队列是否为空。如果队列非空,则将尾部标记-1加入活结点队列,算法开始处理下一层的活结点。

while (true)
{if (ew + w[i] <= c) enQueue(ew + w[i], i); // 检查左儿子结点(x[i]=1)enQueue(ew, i); //右儿子结点总是可行的(x[i]=0)ew = ((Integer) queue.remove()).intValue(); // 取下一扩展结点if (ew == -1) // 同层结点尾部标识(是否队尾呢?){if (queue.isEmpty()) return bestw;queue.put(new Integer(-1)); // 同层结点尾部标识ew = ((Integer) queue.remove()).intValue(); // 取下一扩展结点i++; // 进入下一层 }
}

采用队列式分支限界法求解 算法改进

设bestw是当前最优解;ew是当前扩展结点所相应的重量;r是剩余集装箱的重量。则当ew+r≤ bestw时,可将其右子树剪去。
为了确保右子树成功剪枝,应该在算法每一次进入左子树的时候更新bestw的值。

// 检查左儿子结点
int wt = ew + w[i];
if (wt <= c)
{ 	// 可行结点if (wt > bestw) bestw = wt;   // 提前更新bestw// 加入活结点队列if (i < n)queue.put(new Integer(wt));
}// 检查右儿子结点
if (ew + r > bestw && i < n)  // 右儿子剪枝
// 可能含最优解
queue.put(new Integer(ew));
ew=((Integer)queue.remove())
.intValue();
// 取下一扩展结点

构造最优解
为了在算法结束后能方便地构造出与最优值相应的最优解,算法必须存储相应子集树中从活结点到根结点的路径。为此目的,可在每个结点处设置指向其父结点的指针,并设置左、右儿子标志。

private static class QNode
{ QNode parent; // 父结点
boolean leftChild; // 左儿子标识
int weight; // 结点所相应的载重量

找到最优值后,可以根据parent回溯到根节点,找到最优解。

// 构造当前最优解
for (int j = n; j > 0; j--)
{ bestx[j] = (e.leftChild) ? 1 : 0;
e = e.parent;
}

采用优先队列式分支限界法求解

求解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。
用最大堆表示活结点优先队列。

活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。

  1. 优先队列中优先级最大的活结点成为下一个扩展结点
  2. 以结点x为根的子树中所有结点相应的路径的载重量不超过它的优先级
  3. 子集树中叶结点所相应的载重量与其优先级相同

在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解。此时可终止算法。

四、总结

(1) 适于求解组合搜索问题及优化问题
(2) 解的表示:解向量,求解是不断扩充解向量的过程
(3) 分支策略:广度优先
(4) 限界函数(约束条件、优化问题的代价函数)
(5) 扩展结点处理方式
队列式
优先队列式


http://www.hkcw.cn/article/cZIvuDWuyi.shtml

相关文章

【springcloud】快速搭建一套分布式服务springcloudalibaba(四)

第四篇 基于nacos搭建分布式项目 分布式系统日志&#xff08;skywalkinges&#xff09; 项目所需 maven nacos java8 idea git mysql redis skywalking es 本文主要从客户下单时扣减库存的操作&#xff0c;将链路日志模拟出来&#xff0c;网关系统/用户系统/商品系统/订…

设计模式(行为型)-中介者模式

目录 定义 类图结构展示 角色职责详解 模式的优缺点分析 优点 缺点 适用场景 应用实例 与其他模式的结合与拓展 总结 定义 中介者模式的核心思想可以概括为&#xff1a;用一个中介对象来封装一系列的对象交互。这个中介者就像一个通信枢纽&#xff0c;使各对象不需要…

PMOS以及电源转换电路设计

PMOS的使用 5V_EN5V时&#xff0c;PMOS截止&#xff1b; 5V_EN0V时&#xff0c;PMOS导通&#xff1b; 电源转换电路 当Vout0V时&#xff0c;Vg0V, Vgs>Vth, PMOS导通&#xff0c;只有电池供电&#xff1b; 当Vout5V时&#xff0c;Vg4.9V, Vs4.8V?, Vgs<Vth, PMOS截止&am…

本地部署 DeepSeek R1(最新)【从下载、安装、使用和调用一条龙服务】

文章目录 一、安装 Ollama1.1 下载1.2 安装 二、下载 DeepSeek 模型三、使用 DeepSeek3.1 在命令行环境中使用3.2 在第三方软件中使用 一、安装 Ollama 1.1 下载 官方网址&#xff1a;Ollama 官网下载很慢&#xff0c;甚至出现了下载完显示 无法下载&#xff0c;需要授权 目…

数据治理的演变与AI趋势

知识星球&#xff1a;数据书局。打算通过知识星球将这些年积累的知识分享出来&#xff0c;让各位在数据治理、数据分析的路上少走弯路&#xff0c;另外星球也方便动态更新最近的资料&#xff0c;提供各位一起讨论数据的小圈子 1.数据治理的演变 1.1.摘要 数据治理是指组织管…

Fullstack 面试复习笔记:操作系统 / 网络 / HTTP / 设计模式梳理

Fullstack 面试复习笔记&#xff1a;操作系统 / 网络 / HTTP / 设计模式梳理 面试周期就是要根据JD调整准备内容&#xff08;挠头&#xff09;&#xff0c;最近会混合复习针对全栈这块的内容&#xff0c;目前是根据受伤的JD&#xff0c;优先选择一些基础的操作系统、Java、Nod…

【MIMO稳定裕度】基于数据驱动的多输入多输出系统稳定裕度分析

最近一直在忙着写论文&#xff0c;只能说要写一篇高水平论文确实不容易&#xff0c;要一直反复来回修改调整&#xff0c;要求比较高&#xff0c;所以没太有时间和精力写博客&#xff0c;这两天结束了初稿&#xff0c;又正好是假期&#xff0c;出来冒个泡。 本次分享的主题是&am…

Python 训练营打卡 Day 33-神经网络

简单神经网络的流程 1.数据预处理&#xff08;归一化、转换成张量&#xff09; 2.模型的定义 继承nn.Module类 定义每一个层 定义前向传播流程 3.定义损失函数和优化器 4.定义训练过程 5.可视化loss过程 预处理补充&#xff1a; 分类任务中&#xff0c;若标签是整…

TDengine 的 AI 应用实战——电力需求预测

作者&#xff1a; derekchen Demo数据集准备 我们使用公开的UTSD数据集里面的电力需求数据&#xff0c;作为预测算法的数据来源&#xff0c;基于历史数据预测未来若干小时的电力需求。数据集的采集频次为30分钟&#xff0c;单位与时间戳未提供。为了方便演示&#xff0c;按…

【03】完整开发腾讯云播放器SDK的UniApp官方UTS插件——优雅草上架插件市场-卓伊凡

【03】完整开发腾讯云播放器SDK的UniApp官方UTS插件——优雅草上架插件市场-卓伊凡 一、项目背景与转型原因 1.1 原定计划的变更 本系列教程最初规划是开发即构美颜SDK的UTS插件&#xff0c;但由于甲方公司内部战略调整&#xff0c;原项目被迫中止。考虑到&#xff1a; 技术…

(aaai2024) Omni-Kernel Network for Image Restoration

代码&#xff1a;https://github.com/c-yn/OKNet 研究动机&#xff1a;作者认为Transformer模型计算复杂度太高&#xff0c;因此提出了 omni-kernel module &#xff08;OKM&#xff09;&#xff0c;可以有效的学习局部到全局的特征表示。该模块包括&#xff1a;全局、大分支、…

useMemo useCallback 自定义hook

useMemo & useCallback & 自定义hook useMemo 仅当依赖项发生变化的时候&#xff0c;才去重新计算&#xff1b;其他状态变化时则不去做不必要的计算。 useCallback 缓存函数。但是使用注意&#x1f4e2; &#xff0c;useCallback没有特别明显的优化。 *合适的场景——父…

android binder(二)应用层编程实例

一、binder驱动浅析 从上图看出&#xff0c;binder的通讯主要涉及三个步骤。 在 Binder Server 端定义好服务&#xff0c;然后向 ServiceManager 注册服务在 Binder Client 中向 ServiceManager 获取到服务发起远程调用&#xff0c;调用 Binder Server 中定义好的服务 整个流…

GESP2024年3月认证C++二级( 第三部分编程题(2)小杨的日字矩阵)

参考程序&#xff1a; #include <iostream> using namespace std;int main() {int n;cin >> n; // 读入奇数 n// 外层循环控制每一行for (int i 0; i < n; i) {// 内层循环控制每一列for (int j 0; j < n; j) {char ch;// 如果当前列是最左或最右&#x…

BUUCTF[ACTF2020 新生赛]Exec 1题解

BUUCTF[ACTF2020 新生赛]Exec 1题解 分析解题过程总结: 分析 先分析题目&#xff1a;exc()是一个内部调用shell命令的函数&#xff0c;同样的函数还有system(), 创建靶机&#xff0c;打开网址&#xff0c;是一个和PING相关的网页&#xff0c;查看源代码&#xff0c;没有提示&a…

NX869NX874美光固态颗粒NX877NX883

NX869NX874美光固态颗粒NX877NX883 美光固态硬盘颗粒技术解析与市场展望 近年来&#xff0c;固态硬盘&#xff08;SSD&#xff09;市场呈现出蓬勃发展的态势&#xff0c;而作为核心组件的存储颗粒&#xff0c;其技术进展与市场动态自然吸引了众多关注。在众多品牌中&#xff…

CodeTop100 Day20

58、翻转字符串中的数字 class Solution {public String reverseWords(String s) {s s.trim(); int j s.length() - 1, i j;StringBuilder res new StringBuilder();while (i > 0) {while (i > 0 && s.charAt(i) ! ) i--…

重温经典算法——快速排序

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 基本原理 快速排序基于分治思想&#xff0c;通过选取基准元素将数组划分为两个子数组&#xff08;小于基准和大于基准&#xff09;&#xff0c;递归排序子数组。平均时间复…

【机器学习】集成学习与梯度提升决策树

目录 一、引言 二、自举聚合与随机森林 三、集成学习器 四、提升算法 五、Python代码实现集成学习与梯度提升决策树的实验 六、总结 一、引言 在机器学习的广阔领域中,集成学习(Ensemble Learning)犹如一座闪耀的明星,它通过组合多个基本学习器的力量,创造出…

Python量化交易:K线形态识别与技术分析可视化

引言 在量化交易领域&#xff0c;K线形态识别是一种重要的技术分析方法&#xff0c;可以帮助投资者预测市场趋势并制定交易策略。本文将介绍如何使用Python实现K线形态的自动识别与可视化分析&#xff0c;无需依赖复杂的第三方库如TA-Lib&#xff0c;完全使用纯Python实现。通…