分枝定界法讲义_代码
- 格式:doc
- 大小:48.50 KB
- 文档页数:6
Python 分支定界法1. 介绍分支定界法是一种在计算机科学中常用的算法解决方法,用于在搜索问题中确定解的范围。
在这种方法中,问题被划分为多个子问题,通过评估每个子问题的边界条件来确定是否需要进一步搜索。
这种方法通常用于解决优化问题、搜索问题和决策问题。
在Python中,我们可以使用分支定界法来解决各种问题,包括图搜索、最短路径、最小生成树等。
本文将介绍分支定界法的基本原理和在Python中的应用。
2. 基本原理分支定界法的基本原理是将问题划分为多个子问题,并通过对每个子问题进行评估来确定解的范围。
在每个子问题中,我们可以使用一些启发式方法来估计解的上界和下界,从而确定是否需要进一步搜索。
通过逐步缩小解的范围,我们可以提高算法的效率并找到最优解。
3. 分支定界法的应用3.1 图搜索分支定界法在图搜索中的应用非常广泛。
在图搜索问题中,我们需要找到从一个节点到另一个节点的最短路径或最小代价路径。
通过使用分支定界法,我们可以根据当前路径的代价和启发式方法来估计剩余路径的代价,并根据这些估计值来选择下一个节点进行搜索。
这种方法可以大大减少搜索的空间,并找到最优解。
3.2 最短路径最短路径问题是图搜索问题的一个特例,它要求找到从一个节点到另一个节点的最短路径。
在分支定界法中,我们可以使用启发式方法来估计剩余路径的代价,并根据这些估计值来选择下一个节点进行搜索。
通过不断更新路径的代价和选择最优节点,我们可以找到最短路径。
3.3 最小生成树最小生成树问题是在一个连通图中找到一棵包含所有节点的子图,并使得子图的边的权重之和最小。
分支定界法可以用于解决最小生成树问题。
通过选择边的权重最小的节点进行搜索,并使用启发式方法来估计剩余节点的权重和,我们可以找到最小生成树。
4. Python中的分支定界法在Python中,我们可以使用分支定界法来解决各种问题。
以下是使用分支定界法的一般步骤:1.定义问题的状态和边界条件。
§3.3 分支定界法引入:复习割平面的思想⎪⎪⎩⎪⎪⎨⎧≥=为整数向量x x b Ax t s x c T 0..min (P ) ⎪⎩⎪⎨⎧≥=0..min x b Ax t s x c T (P 0)1、分支定界法的基本思想⎪⎪⎩⎪⎪⎨⎧≥≤为整数向量x x b Ax t s x c T 0..min(P ) ⎪⎩⎪⎨⎧≥≤0..min x b Ax t s x c T (P 0)设原问题(P )的松弛问题(P 0)的最优解为0x 。
若 0x 的某个分量 0i x 不是整数,由于(P )的整数最优解的第i 个分量必定落在区域 ][0i i x x ≤ 或 1][0+≥i i x x 中,因此可将原问题(P )分解为两个子问题来求解,⎪⎪⎩⎪⎪⎨⎧≤≥≤][,0..min 0i i T x x x x b Ax t s x c 为整数向量(P 1) ⎪⎪⎩⎪⎪⎨⎧+≥≥≤1][,0..min 0i i T x x x x bAx t s x c 为整数向量(P 2)某个点不需要分支的条件:(1)相应的松弛问题的最优解是整数向量,或(2)相应的松弛问题没有可行解记 1x c z T m =, 若 m T z x c ≥2,则对 )(2P 的任何一个整数可行解 x 都有 m T T z x c x c ≥≥2x 1][0+≥i i x x ][0i i x x ≤)(1P )(2P 2x 或无解整数1x ][2k k x x ≤1][2+≥k k x x )(3P )(4P 3x 或无解整数4x ][3l l x x ≤1][3+≥l l x x )(1P )(6P )(P所以原问题的最优整数解一定不在)(2P 中,所以对 )(2P 就无须继续分支,在这种情况下,我们说)(2P 被剪枝,并把它称为死点(也称为已查明),尚未查明的点称为活点。
若 m T z x c <2,则对 )(2P 需继续考察。
function [newx,newfval,status,newbound] = branchbound(f,A,B,I,x,fval,bound,Aeq,Beq,lb,ub,e)% 分支定界法求解整数规划% f,A,B,Aeq,Beq,lb,ub与线性规划相同% I为整数限制变量的向量% x为初始解,fval为初始值options = optimset('display','off');[x0,fval0,status0]=linprog(f,A,B,Aeq,Beq,lb,ub,[],options);%递归中的最终退出条件%无解或者解比现有上界大则返回原解if status0 <= 0 || fval0 >= boundnewx = x;newfval = fval;newbound = bound;status = status0;return;end%是否为整数解,如果是整数解则返回intindex = find(abs(x0(I) - round(x0(I))) > e);if isempty(intindex)newx(I) = round(x0(I));newfval = fval0;newbound = fval0;status = 1;return;end%当有非整可行解时,则进行分支求解%此时必定会有整数解或空解%找到第一个不满足整数要求的变量n = I(intindex(1));addA = zeros(1,length(f));addA(n) = 1;%构造第一个分支 x<=floor(x(n))A = [A;addA];B = [B,floor(x(n))];[x1,fval1,status1,bound1] = branchbound(f,A,B,I,x0,fval0,bound,Aeq,Beq,lb,ub,e);A(end,:) = [];B(:,end) = [];%解得第一个分支,若为更优解则替换,若不是则保持原状status = status1;if status1 > 0 && bound1 < boundnewx = x1;newfval = fval1;bound = fval1;newbound = bound1;elsenewx = x0;newfval = fval0;newbound = bound;end%构造第二分支A = [A;-addA];B = [B,-ceil(x(n))];[x2,fval2,status2,bound2] = branchbound(f,A,B,I,x0,fval0,bound,Aeq,Beq,lb,ub,e);A(end,:) = [];B(:,end) = [];%解得第二分支,并与第一分支做比较,如果更优则替换if status2 > 0 && bound2 < boundstatus = status2;newx = x2;newfval = fval2;newbound = bound2;end。
分支定界是一种用于解决整数线性规划问题的算法。
它首先尝试找到一个可行的解决方案,然后使用分支定界方法逐步改进这个解决方案,直到找到最优解。
以下是一个简单的Python实现分支定界算法的代码:import numpy as npfrom scipy.optimize import linprog# 定义目标函数def f(x):return -x[0] - x[1]# 定义约束条件A = [[1, 1, -1, -1], [1, -1, 1, -1], [1, 1, 1, -1]]b = [1, -1, 2]# 定义初始可行解x0 = np.array([0, 0])# 定义分支定界函数def branch_and_bound(A, b, f, x0):# 计算初始最优解c = np.array(f.tolist()).astype(float)res = linprog(c, A_ub=A, b_ub=b)best_val = res.funbest_x = res.xprint(f"当前最优解:{best_x}, 当前最优值:{best_val}")# 生成分支变量branch_var = []for i in range(len(A)):if A[i][0] > 0: # 取大于0的变量作为分支变量branch_var.append(i)# 分支定界过程while branch_var:i = branch_var.pop(0) # 选择第一个分支变量进行分支# 分支一:取大于等于0的变量作为新的可行解new_x = best_x.copy()new_x[i] = max(0, best_x[i])new_val = f(new_x) # 计算新解的目标函数值if new_val < best_val: # 如果新解更优,则更新最优解和最优值best_val = new_valbest_x = new_xprint(f"新最优解:{best_x}, 新最优值:{best_val}")# 分支二:取小于0的变量作为新的可行解new_x = best_x.copy()new_x[i] = min(0, best_x[i])new_val = f(new_x) # 计算新解的目标函数值if new_val < best_val: # 如果新解更优,则更新最优解和最优值best_val = new_valbest_x = new_xprint(f"新最优解:{best_x}, 新最优值:{best_val}") return best_x, best_val在上面的代码中,我们首先定义了目标函数f和约束条件A、b。
第5 章分枝定界任何美好的事情都有结束的时候。
现在我们学习的是本书的最后一章。
幸运的是,本章用到的大部分概念在前面各章中已作了介绍。
类似于回溯法,分枝定界法在搜索解空间时,也经常使用树形结构来组织解空间(常用的树结构是第1 6章所介绍的子集树和排列树)。
然而与回溯法不同的是,回溯算法使用深度优先方法搜索树结构,而分枝定界一般用宽度优先或最小耗费方法来搜索这些树。
本章与第1 6章所考察的应用完全相同,因此,可以很容易比较回溯法与分枝定界法的异同。
相对而言,分枝定界算法的解空间比回溯法大得多,因此当内存容量有限时,回溯法成功的可能性更大。
算法思想分枝定界(branch and bound)是另一种系统地搜索解空间的方法,它与回溯法的主要区别在于对E-节点的扩充方式。
每个活节点有且仅有一次机会变成E-节点。
当一个节点变为E-节点时,则生成从该节点移动一步即可到达的所有新节点。
在生成的节点中,抛弃那些不可能导出(最优)可行解的节点,其余节点加入活节点表,然后从表中选择一个节点作为下一个E-节点。
从活节点表中取出所选择的节点并进行扩充,直到找到解或活动表为空,扩充过程才结束。
有两种常用的方法可用来选择下一个E-节点(虽然也可能存在其他的方法):1) 先进先出(F I F O)即从活节点表中取出节点的顺序与加入节点的顺序相同,因此活节点表的性质与队列相同。
2) 最小耗费或最大收益法在这种模式中,每个节点都有一个对应的耗费或收益。
如果查找一个具有最小耗费的解,则活节点表可用最小堆来建立,下一个E-节点就是具有最小耗费的活节点;如果希望搜索一个具有最大收益的解,则可用最大堆来构造活节点表,下一个E-节点是具有最大收益的活节点。
例5-1 [迷宫老鼠] 考察图16-3a 给出的迷宫老鼠例子和图1 6 - 1的解空间结构。
使用F I F O分枝定界,初始时取(1,1)作为E-节点且活动队列为空。
迷宫的位置( 1 , 1)被置为1,以免再次返回到这个位置。
(1,1)被扩充,它的相邻节点(1,2)和(2,1)加入到队列中(即活节点表)。
为避免再次回到这两个位置,将位置(1,2)和(2,1)置为1。
此时迷宫如图1 7 - 1 a所示,E-节点(1,1)被删除。
节点(1,2)从队列中移出并被扩充。
检查它的三个相邻节点(见图1 6 - 1的解空间),只有(1,3)是可行的移动(剩余的两个节点是障碍节点),将其加入队列,并把相应的迷宫位置置为1,所得到的迷宫状态如图17-1b 所示。
节点(1,2)被删除,而下一个E-节点(2,1)将会被取出,当此节点被展开时,节点(3,1)被加入队列中,节点(3,1)被置为1,节点(2,1)被删除,所得到的迷宫如图17-1c 所示。
此时队列中包含(1,3)和(3,1)两个节点。
随后节点(1,3)变成下一个E-节点,由于此节点不能到达任何新的节点,所以此节点即被删除,节点(3,1)成为新的E-节点,将队列清空。
节点(3,1)展开,(3,2)被加入队列中,而(3,1)被删除。
(3,2)变为新的E-节点,展开此节点后,到达节点(3,3),即迷宫的出口。
使用F I F O搜索,总能找出从迷宫入口到出口的最短路径。
需要注意的是:利用回溯法找到的路径却不一定是最短路径。
有趣的是,程序6 - 11已经给出了利用F I F O分枝定界搜索从迷宫的(1,1)位置到(n,n)位置的最短路径的代码。
例5-2 [0/1背包问题] 下面比较分别利用F I F O分枝定界和最大收益分枝定界方法来解决如下背包问题:n=3,w=[20,15,15], p=[40,25,25], c= 3 0。
F I F O分枝定界利用一个队列来记录活节点,节点将按照F I F O顺序从队列中取出;而最大收益分枝定界使用一个最大堆,其中的E-节点按照每个活节点收益值的降序,或是按照活节点任意子树的叶节点所能获得的收益估计值的降序从队列中取出。
本例所使用的背包问题与例1 6 . 2相同,并且有相同的解空间树。
使用F I F O分枝定界法搜索,初始时以根节点A作为E-节点,此时活节点队列为空。
当节点A展开时,生成了节点B和C,由于这两个节点都是可行的,因此都被加入活节点队列中,节点A被删除。
下一个E-节点是B,展开它并产生了节点D和E,D是不可行的,被删除,而E被加入队列中。
下一步节点C成为E-节点,它展开后生成节点F和G,两者都是可行节点,加入队列中。
下一个E-节点E生成节点J和K,J不可行而被删除,K是一个可行的叶节点,并产生一个到目前为止可行的解,它的收益值为4 0。
下一个E-节点是F,它产生两个孩子L、M,L代表一个可行的解且其收益值为5 0,M代表另一个收益值为1 5的可行解。
G是最后一个E-节点,它的孩子N和O都是可行的。
由于活节点队列变为空,因此搜索过程终止,最佳解的收益值为5 0。
可以看到,工作在解空间树上的F I F O分枝定界方法非常象从根节点出发的宽度优先搜索。
它们的主要区别是在F I F O分枝定界中不可行的节点不会被搜索。
最大收益分枝定界算法以解空间树中的节点A作为初始节点。
展开初始节点得到节点B和C,两者都是可行的并被插入堆中,节点B获得的收益值是4 0(设x1 = 1),而节点C得到的收益值为0。
A被删除,B 成为下一个E-节点,因为它的收益值比C的大。
当展开B时得到了节点D和E,D是不可行的而被删除,E加入堆中。
由于E具有收益值4 0,而C为0,因为E成为下一个E-节点。
展开E时生成节点J和K,J不可行而被删除,K是一个可行的解,因此K为作为目前能找到的最优解而记录下来,然后K被删除。
由于只剩下一个活节点C在堆中,因此C作为E-节点被展开,生成F、G两个节点插入堆中。
F的收益值为2 5,因此成为下一个E-节点,展开后得到节点L和M,但L、M都被删除,因为它们是叶节点,同时L所对应的解被作为当前最优解记录下来。
最终,G成为E-节点,生成的节点为N和O,两者都是叶节点而被删除,两者所对应的解都不比当前的最优解更好,因此最优解保持不变。
此时堆变为空,没有下一个E-节点产生,搜索过程终止。
终止于J的搜索即为最优解。
犹如在回溯方法中一样,可利用一个定界函数来加速最优解的搜索过程。
定界函数为最大收益设置了一个上限,通过展开一个特殊的节点可能获得这个最大收益。
如果一个节点的定界函数值不大于目前最优解的收益值,则此节点会被删除而不作展开,更进一步,在最大收益分枝定界方法中,可以使节点按照它们收益的定界函数值的非升序从堆中取出,而不是按照节点的实际收益值来取出。
这种策略从可能到达一个好的叶节点的活节点出发,而不是从目前具有较大收益值的节点出发。
例5-3 [旅行商问题] 对于图1 6 - 4的四城市旅行商问题,其对应的解空间为图1 6 - 5所示的排列树。
F I F O分枝定界使用节点B作为初始的E-节点,活节点队列初始为空。
当B展开时,生成节点C、D和E。
由于从顶点1到顶点2,3,4都有边相连,所以C、D、E三个节点都是可行的并加入队列中。
当前的E-节点B被删除,新的E-节点是队列中的第一个节点,即节点C。
因为在图1 6 - 4中存在从顶点2到顶点3和4的边,因此展开C,生成节点F和G,两者都被加入队列。
下一步,D成为E-节点,接着又是E,到目前为止活节点队列中包含节点F到K。
下一个E-节点是F,展开它得到了叶节点L。
至此找到了一个旅行路径,它的开销是5 9。
展开下一个E-节点G,得到叶节点M,它对应于一个开销为6 6的旅行路径。
接着H 成为E-节点,从而找到叶节点N,对应开销为2 5的旅行路径。
下一个E-节点是I,它对应的部分旅行1 - 3 - 4的开销已经为2 6,超过了目前最优的旅行路径,因此,I不会被展开。
最后,节点J,K成为E-节点并被展开。
经过这些展开过程,队列变为空,算法结束。
找到的最优方案是节点N所对应的旅行路径。
如果不使用F I F O方法,还可以使用最小耗费方法来搜索解空间树,即用一个最小堆来存储活节点。
这种方法同样从节点B开始搜索,并使用一个空的活节点列表。
当节点B展开时,生成节点C、D和E并将它们加入最小堆中。
在最小堆的节点中,E具有最小耗费(因为1 - 4的局部旅行的耗费是4),因此成为E-节点。
展开E生成节点J和K并将它们加入最小堆,这两个节点的耗费分别为1 4和2 4。
此时,在所有最小堆的节点中,D具有最小耗费,因而成为E-节点,并生成节点H和I。
至此,最小堆中包含节点C、H、I、J和K,H具有最小耗费,因此H成为下一个E-节点。
展开节点E,得到一个完整的旅行路径1 - 3 - 2 - 4 - 1,它的开销是2 5。
节点J是下一个E-节点,展开它得到节点P,它对应于一个耗费为2 5的旅行路径。
节点K和I是下两个E-节点。
由于I的开销超过了当前最优的旅行路径,因此搜索结束,而剩下的所有活节点都不能使我们找到更优的解。
对于例5 - 2的背包问题,可以使用一个定界函数来减少生成和展开的节点数量。
这种函数将确定旅行的最小耗费的下限,这个下限可通过展开某个特定的节点而得到。
如果一个节点的定界函数值不能比当前的最优旅行更小,则它将被删除而不被展开。
另外,对于最小耗费分枝定界,节点按照它在最小堆中的非降序取出。
在以上几个例子中,可以利用定界函数来降低所产生的树型解空间的节点数目。
当设计定界函数时,必须记住主要目的是利用最少的时间,在内存允许的范围内去解决问题。
而通过产生具有最少节点的树来解决问题并不是根本的目标。
因此,我们需要的是一个能够有效地减少计算时间并因此而使产生的节点数目也减少的定界函数。
回溯法比分枝定界在占用内存方面具有优势。
回溯法占用的内存是O(解空间的最大路径长度),而分枝定界所占用的内存为O(解空间大小)。
对于一个子集空间,回溯法需要(n)的内存空间,而分枝定界则需要O ( 2n ) 的空间。
对于排列空间,回溯需要(n) 的内存空间,分枝定界需要O (n!) 的空间。
虽然最大收益(或最小耗费)分枝定界在直觉上要好于回溯法,并且在许多情况下可能会比回溯法检查更少的节点,但在实际应用中,它可能会在回溯法超出允许的时间限制之前就超出了内存的限制。
练习1. 假定在一个L I F O分枝定界搜索中,活节点列表的行为与堆栈相同,请使用这种方法来解决例5 - 2的背包问题。
L I F O 分枝定界与回溯有何区别?2. 对于如下0 / 1背包问题:n=4, p=[4,3,2,1], w=[1,2,3,4], c =6。
1) 画出有四个对象的背包问题的解空间树。
2) 像例1 7 - 2那样,描述用F I F O分枝定界法解决上述问题的过程。
3) 使用程序1 6 - 6的B o u n d函数来计算子树上任一叶节点可能获得的最大收益值,并根据每一步所能得到的最优解对应的定界函数值来判断是否将节点加入活节点列表中。