关于回溯与分支限界的一些介绍

这篇文章将介绍回溯算法与分支限界算法的有关概念、具体应用及代码等内容。

一、回溯法

1.1 概念

回溯法是一种试探法,所以它也叫试探算法。它尝试构建问题的解,并且在发现解不满足条件的时候撤销选择(即“回溯”),尝试其他路径来寻找可行解。

1.2 举例

有一不等式如下:

3x +4y+2z\leq12

求符合条件的所有整数解。

你们对于这个问题我们如果采用回溯的算法则可以有如下的代码:

def save(x, y, z):if 3 * x + 4 * y + 2 * z > 12:return Falseelse:print(x, y, z)# 处理 x, y, z 的递增情况,并递归调用if x < 5:save(x + 1, y, z)if y < 4:save(x, y + 1, z)if z < 7:save(x, y, z + 1)if __name__ == '__main__':save(0, 0, 0)

关于代码的输出因为太多了,所以就不给出了。不过在这里可以给出它的搜索树:

      (0, 0, 0)/ | \/  |  \(1,0,0) (0,1,0) (0,0,1)/  \     /  \     /  \
(2,0,0)(1,1,0)(0,2,0)(0,1,1)(0,0,2)/   \    ...    ...

我们分析这个代码,发现它与遍历有些相似,但不同的是,在遍历中,如果遇到了不符合的结果时代码还将以往之前,不撞南墙不后悔,但回溯不同,它在遇到不符合条件的情况时就直接停止了,不再继续浪费资源去运算了。 即它在发现总和在大于目标总和时会及时停止,而在达到目标要求时会输出结果,同时在每个解上进行递增以至到达解允许达到的最大边界。

我们再给出一个例子,这是一个关于4*4的拉丁方(Latin)的例子,要求在一个4*4的格子中填入1,2,3,4以使得每一行每一列都没有重复的数字。例如如下的这个拉丁方:

1 2 3 4
3 4 1 2
4 3 2 1
2 1 4 3

那么我们可以给出如下的代码: 

def is_valid(square, row, col, num):# 检查同一行是否有重复for i in range(4):if square[row][i] == num:return False# 检查同一列是否有重复for i in range(4):if square[i][col] == num:return Falsereturn Truedef backtrack(square, row, col):if col == 4:backtrack(square, row + 1, 0)returnif row == 4:print_square(square)  # 打印解决方案returnfor num in range(1, 5):if is_valid(square, row, col, num):square[row][col] = numbacktrack(square, row, col + 1)square[row][col] = 0  # 回溯def print_square(square):path=r"C:\Users\20349\Desktop\Studing\studying\算法设计与分析\homework\Latin.txt"with open(path,'a') as result:for row in square:result.write(" ".join(map(str, row))+"\n")result.write("\n")square = [[0]*4 for _ in range(4)]
backtrack(square, 0, 0)
1.3 算法步骤总结

我们可以总结出步骤如下:

1.定义状态表示,即如何表示当前问题的部分解;

2.定义终止条件,确定何时到达问题的解空间边界,即何时停止搜索;

3.定义递归的框架,并确定如何对解进行扩展;

4.检查状态的有效性,即检查新生成的状态是否符合给定的约束条件,若不符合则及时回溯;

5.记录解,通过输出、储存等方式将当前得到的解及时记录下来;

6.回溯,当搜索无法前进时(即达到叶子节点或发现当前路径不可行),回溯到上一个状态;

7.剪枝,在搜索过程中,尽可能早地剪除那些不可能导致最优解的分支,这可以通过设定上下界、预估解的质量等方式实现。

1.4 决策树

在回溯算法中,我们是可以通过决策树来展示整个算法求解的过程的。决策树是一种监督学习方法,它通过一系列的判断(节点)来做出分类或回归决策。每一个节点代表一个判断标准,而每条分支代表了基于这一标准的不同结果。最终,沿着决策树的路径,我们会到达叶节点,即最终的决策结果。

我在之前曾经写过关于决策树的文章,不了解的可以回看那篇文章,此外,我再在这里给出一个决策树的代码:

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt# 加载数据集
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
target_names = iris.target_names# 创建决策树分类器
clf = DecisionTreeClassifier(random_state=42)
clf.fit(X, y)# 可视化决策树
plt.figure(figsize=(40, 20))
plot_tree(clf, filled=True, feature_names=feature_names, class_names=target_names, fontsize=10)
plt.title("Decision Tree for Iris Dataset")
plt.show()

代码绘制的决策树如下: 

不过机器学习的决策树与回溯算法里的决策树还是存在一些不同之处的:

首先在机器学习中,决策树是为了分类和回归任务,通过对数据进行分割,找到最优的决策规则,从而对新数据进行预测。而在回溯算法中,它用于搜索问题的解空间,通过回溯法逐步尝试不同的决策路径,直到找到满足条件的解或确定没有解;

此外在构建上也有所不同,机器学习中,决策树是通过递归地选择最佳特征进行分割,通常使用信息增益、基尼不纯度等指标来评估特征的重要性。而回溯算法中则是通过递归地尝试不同的决策路径,如果某条路径不满足条件,则回溯到上一个节点,尝试其他路径;

然后在停止条件上,机器学习中通常基于数据集的纯度(如所有样本属于同一类)、树的深度、最小样本数等。而在回溯算法里通常基于问题的具体要求,如找到一个解、遍历完所有可能的路径等。

最后,我在这里给出上述的Latin方的绘制决策树的代码:

import networkx as nx
import matplotlib.pyplot as plt# 记录决策路径的字典
decision_tree = {}
current_path = []def is_valid(square, row, col, num):# 检查同一行是否有重复for i in range(4):if square[row][i] == num:return False# 检查同一列是否有重复for i in range(4):if square[i][col] == num:return Falsereturn Truedef backtrack(square, row, col, parent=None):if col == 4:backtrack(square, row + 1, 0, parent)returnif row == 4:print_square(square)  # 打印解决方案returnfor num in range(1, 5):if is_valid(square, row, col, num):square[row][col] = numcurrent_path.append((row, col, num))node_id = hash(tuple(map(tuple, square)))decision_tree[node_id] = (tuple(map(tuple, square)), parent)backtrack(square, row, col + 1, node_id)current_path.pop()square[row][col] = 0  # 回溯def print_square(square):path = r"C:\Users\20349\Desktop\Studing\studying\算法设计与分析\homework\Latin.txt"with open(path, 'a') as result:for row in square:result.write(" ".join(map(str, row)) + "\n")result.write("\n")# 初始化拉丁方阵
square = [[0] * 4 for _ in range(4)]
backtrack(square, 0, 0)# 构建树结构
G = nx.DiGraph()
for node_id, (state, parent) in decision_tree.items():G.add_node(node_id, state=state)if parent is not None:G.add_edge(parent, node_id)# 绘制树
plt.figure(figsize=(12, 8))
pos = nx.spring_layout(G, k=0.5, iterations=20)
node_labels = {node: str(state) for node, state in nx.get_node_attributes(G, 'state').items()}
nx.draw(G, pos, with_labels=True, labels=node_labels, node_size=3000, node_color='lightblue', font_size=10, font_weight='bold')
plt.title("Decision Tree for Latin Square Backtracking")
plt.show()

因为这个的绘制太过复杂,最后的结果不易展示,所以就不给出图像了。

二、分支限界

分支限界法和回溯法都是可以用于进行组合优化求解的,在某种程度上,可以说分支限界法是回溯法的一种变种,不过也存在一些不同之处。

首先我们来看它的相似之处:它们都通过树形结构来表示问题的解空间,然后也都通过递归来探索解空间,此外,它们都可以用剪枝技术来减少需要的解空间,以此节约资源。

接下来是不同之处,在具体的剪枝方法上,回溯是依靠是否满足约束条件来进行剪枝,而分支限界法则在考虑是否满足约束条件的同时还通过界限检查来剪枝。计算每个子问题的界限(通常是下界或上界),如果子问题的界限超出已知最优解的界限,则剪枝该子问题;

在探索策略上,回溯一般采用深度优先搜索(DFS)策略,逐层深入,直到找到一个解或确定没有解。而分支限界则考虑多种算法,比如广度优先搜索(BFS)、深度优先搜索(DFS)和优先队列式搜索(Best-First Search);

在寻找解上,回溯更多的是寻找可行解,而分支限界是去寻找最优解。

在这里给出一个分支限界的代码:

class Item:def __init__(self, weight, value):self.weight = weightself.value = valueself.ratio = value / weightdef knapsack_branch_and_bound(capacity, items):items.sort(key=lambda x: x.ratio, reverse=True)best_value = 0queue = [(0, 0, 0)]  # (index, current_weight, current_value)while queue:index, current_weight, current_value = queue.pop(0)if index < len(items):item = items[index]# 如果当前物品可以加入背包if current_weight + item.weight <= capacity:new_value = current_value + item.valuenew_weight = current_weight + item.weightif new_value > best_value:best_value = new_valuequeue.append((index + 1, new_weight, new_value))# 不选择当前物品的情况queue.append((index + 1, current_weight, current_value))return best_value# 测试代码
if __name__ == "__main__":items = [Item(10, 60), Item(20, 100), Item(30, 120)]capacity = 50max_value = knapsack_branch_and_bound(capacity, items)print("The maximum value that can be put in the knapsack is", max_value)

代码的运行结果为:

The maximum value that can be put in the knapsack is 220

这些就是关于回溯与分支限界的一些简单的介绍,因为在这里分支限界仅是对于回溯的一个简单拓展,所以没有过多叙述,但实际上关于分支限界的介绍还应有许多,以后也许会再有所介绍。

此上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day05-Filebeat扩展

Filebeat对接ES加密集群 #查看集群状态 [09:22:25 rootelk1:~]#curl 10.0.0.91:9200/_cat/nodes -u elastic:1234561.编写配置文件 [09:52:06 rootelk3:/etc/filebeat]#vim 12-log-to-es_tls.yamlfilebeat.inputs: - type: logpaths:- /var/log/nginx/access.log*json.keys_un…

Vue全栈开发旅游网项目(3)-Vue路由配置

1. 配置路由 1.1 导入路由工具 文件地址&#xff1a;src\router\index.js import { createRouter, createWebHistory } from vue-router//导入配置路由的工具 import HomeView from ../views/HomeView.vue//导入组件const router createRouter({//创建路由对象history: cre…

代随(136):图论dfs——邻接矩阵

题干&#xff1a; 代码&#xff1a; #include <iostream> #include <vector> using namespace std;vector<vector<int>>res; vector<int>path;void dfs(vector<vector<int>>&graph, int x, int n) {if(x n){res.push_back(pat…

精选录屏软件大赏:满足不同场景需求的录制利器

现在是互联网的世界&#xff0c;所以很多时候学习、工作我们都离不开电脑。在我们通过互联网查看网课、开网络会议的时候是不是会因为速度太快而来不及记录&#xff1f;那我推荐使用一些windows录屏软件来记录所有重点&#xff0c;方便我们后期复盘。 1.FOXIT录屏大师 链接&a…

【python】OpenCV—findContours(4.5)

文章目录 1、功能描述2、原理分析3、代码实现4、效果展示5、完整代码6、参考 1、功能描述 输入图片&#xff0c;计算出图片中的目标到相机间的距离 2、原理分析 用最简单的三角形相似性 已知参数&#xff0c;物体的宽度 W W W&#xff0c;物体到相机的距离 D D D&#xff0…

【华为HCIP实战课程二十八】中间到中间系统协议IS-IS邻居关系排错,网络工程师

一、ISIS邻居关系条件 1、同一层次(比如Level-2路由器不能和Level-1路由器形成邻居关系) 2、同一区域(L1必须同一区域) 3、同一网段 R1和R2之间分别配置如下IP地址和掩码: R1 的接口S1/0/0掩码为/24 R2的接口S1/0/0配置成掩码/28: 此时R1和R2依然可以建立ISIS邻居关系…

文件操作和 IO(一):文件基础知识 文件系统操作 => File类

目录 1. 什么是文件 1.1 概念 1.2 硬盘, 内存, 寄存器之间的区别 1.3 机械硬盘和固态硬盘 2. 文件路径 2.1 绝对路径 2.2 相对路径 3. 文件分类 4. File 类 4.1 属性 4.2 构造方法 4.3 方法 1. 什么是文件 1.1 概念 狭义上的文件: 保存在硬盘上的文件广义的上的文…

Flutter Color 大调整,需适配迁移,颜色不再是 0-255,而是 0-1.0,支持更大色域

在之前的 3.10 里&#xff0c; Flutter 的 Impeller 在 iOS 上支持了 P3 广色域图像渲染&#xff0c;但是当时也仅仅是当具有广色域图像或渐变时&#xff0c;Impeller 才会在 iOS 上显示 P3 的广色域的颜色&#xff0c;而如果你使用的是 Color API&#xff0c;会发现使用的还是…

web自动化测试平台开发之核心执行器

web自动化测试平台开发之核心执行器 一、如何从自动化框架到核心执行器二、核心执行器框架逻辑梳理三、核心执行器利用命令驱动执行 一、如何从自动化框架到核心执行器 脚本:底层用了三个内容:pythonpytestselenium&#xff0c;线性脚本&#xff0c;只是单纯的把功能测试用例转…

Rust 力扣 - 2090. 半径为 k 的子数组平均值

文章目录 题目描述题解思路题解代码题解链接 题目描述 题解思路 半径为 k 的子数组平均值 等价于 子数组长度为2 * k 1的总和 除于 2 * k 1 我们遍历长度为2 * k 1的窗口&#xff0c;我们只需要记录窗口内的平均值即可 题解代码 impl Solution {pub fn get_averages(num…

直接内存、死锁、方法句柄

直接内存 1. 不是虚拟机运行时数据区的一部分&#xff0c;也不是《Java虚拟机规范》中定义的内存区域 2. 直接内存是在Java堆外、直接向系统申请的内存区间 3. 来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存 4. 通常&#xff0c;访问直接内存的速度会优于Java堆&am…

C++《list》

在本篇当中我们将学习STL中的list&#xff0c;在此list就是我们之前在数据结构学习过的链表&#xff0c;在本篇中我们要来了解list当中的成员函数该如何使用&#xff0c;由于list各个函数的接口和之前学习过的vector类型&#xff0c;因此在学习list的使用就较为轻松。在lis篇章…

【牛客刷题实战】二叉树遍历

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 牛客题目&#xff1a; 二叉树遍历 题目描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解题思路 问题理解 算法选择 具体思路 解题要点 完整代码&#xff08;C语言&#xff09; 兄弟们共勉 &#xff01;&…

vmvare启动freebsd操作系统密码忘记了怎么办?

本章教程,主要介绍,通过vmvare安装的freebsd操作系统,密码忘记了,如何重置密码。 一、重启虚拟机 在重启过程中,按键盘中是数字2,进入单用户模式。 二、进入到shell界面 在出现“Enter full pathname of shell or RETURN for /bin/sh:”直接按回车键。 三、输入命令 mou…

【设计模式系列】代理模式(八)

一、什么是代理模式 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它为其他对象提供一种代理以控制对这个对象的访问。代理模式在不直接访问实际对象的情况下&#xff0c;提供了对目标对象的间接访问。通过引入一个代理对象来间接操作实际对…

WPS查询函数VLOOKUP,匹配寻找值自动带入值

想实现在下表输入物料名称后&#xff0c;把上表中的单位自动带入 那就要用到VLOOKUP函数&#xff0c;获取第2个表第1列的值后去第1个表的第1列匹配&#xff0c;匹配到后得到行数值&#xff0c;把第1个表的第2列赋值给第2个表的第2列。 Vlookup函数参数为Vlookup(查找值&#…

sqoop问题汇总记录

此篇博客仅记录在使用sqoop时遇到的各种问题。持续更新&#xff0c;有问题评论区一起探讨&#xff0c;写得有不足之处见谅。 Oracle_to_hive 1. main ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTr…

简单说明vuex

vuex 知识结构配置调用 知识结构 vue用于管理公共数据的仓库 配置 state&#xff1a;所有公共数据的初始状态&#xff08;初始值&#xff09; export default {state: {count: 0,} };mutations&#xff1a;修改state内容的方法&#xff08;必须为同步方法&#xff09; export …

分类算法——决策树 详解

决策树的底层原理 决策树是一种常用的分类和回归算法&#xff0c;其基本原理是通过一系列的简单决策&#xff0c;将数据集划分为多个子集&#xff0c;从而实现分类。决策树的核心思想是通过树形结构表示决策过程&#xff0c;节点代表特征&#xff0c;边代表决策&#xff0c;叶子…

Nature Electronics 用于语音识别的液体声传感器,基于悬浮在载液的钕-铁-硼磁性纳米颗粒

近年来&#xff0c;工程师们开发了一系列越来越复杂的传感器&#xff0c;用于机器人、便携式、可穿戴甚至植入式监测。然后&#xff0c;可以使用最先进的机器学习来分析这些传感器收集的数据&#xff0c;使设备能够识别音频中的特定声音、图像中的对象或其他信息。加州大学洛杉…