今天利用matlab来实现求解完全整数规划问题和混合整数规划问题的分支定界法。
这里求解的模型为目标函数最小化模型:
分支定界法中,通过定界进而进行剪枝,对分支进行了筛选,使我们仅在一部分可行解中寻求最优解,而不是全部穷举出来再寻找,其求解效率更高。
因为分支定界法的求解过程与二叉树结构相同,所以我们利用二叉树这种数据结构来存储每一个线性规划问题的解及对应的目标函数值。
代码如下:
% main.m
% 分支定界法
% -----------------------------------------------
% 求解模型:
% min z = f * x
% A * x <= b
% x >= 0,且为整数
% ---------------------------------------------------
clear global;
clear;
clc;
global result; % 存储所有整数解
global lowerBound; % 下界
global upperBound; % 上界
global count; % 用以判断是否为第一次分支
count = 1;
f = [-40, -90];
A = [8, 7;
7, 20;];
b = [56; 70];
Aeq = [];
beq = [];
lbnd = [0; 0];
ubnd = [inf; inf];
BinTree = createBinTreeNode({f, A, b, Aeq, beq, lbnd, ubnd});
if ~isempty(result)
[fval,flag]=min(result(:,end)); % result中每一行对应一个整数解及对应的函数值
Result=result(flag,:);
disp('该整数规划问题的最优解为:');
disp(Result(1,1:end-1));
disp('该整数规划问题的最优值为:');
disp(Result(1,end));
else
disp('该整数规划问题无可行解');
end
% createBinTreeNode.m
% 构建二叉树,每一结点对应一解
function BinTree = createBinTreeNode(binTreeNode)
global result;
global lowerBound;
global upperBound;
global count;
if isempty(binTreeNode)
return;
else
BinTree{1} = binTreeNode;
BinTree{2} = [];
BinTree{3} = [];
[x, fval, exitflag] = linprog(binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, binTreeNode{6}, binTreeNode{7});
if count == 1
% upperBound = 0; % 初始下界为空
lowerBound = fval;
count = 2;
end
if ~IsInRange(fval)
return;
end
if exitflag == 1
flag = [];
% 寻找非整数解分量
for i = 1 : length(x)
if round(x(i)) ~= x(i)
flag = i;
break;
end
end
% 分支
if ~isempty(flag)
lowerBound = max([lowerBound; fval]);
OutputLowerAndUpperBounds();
lbnd = binTreeNode{6};
ubnd = binTreeNode{7};
lbnd(flag, 1) = ceil(x(flag, 1)); % 朝正无穷四舍五入
ubnd(flag, 1) = floor(x(flag, 1));
% 进行比较,优先选择目标函数较大的进行分支
[~, fval1] = linprog(binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, binTreeNode{6}, ubnd);
[~, fval2] = linprog(binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, lbnd, binTreeNode{7});
if fval1 < fval2
% 创建左子树
BinTree{2} = createBinTreeNode({binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, binTreeNode{6}, ubnd});
% 创建右子树
BinTree{3} = createBinTreeNode({binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, lbnd, binTreeNode{7}});
else
% 创建右子树
BinTree{3} = createBinTreeNode({binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, lbnd, binTreeNode{7}});
% 创建左子树
BinTree{2} = createBinTreeNode({binTreeNode{1}, binTreeNode{2}, binTreeNode{3}, ...
binTreeNode{4}, binTreeNode{5}, binTreeNode{6}, ubnd});
end
else
upperBound = min([upperBound; fval]);
OutputLowerAndUpperBounds();
result = [result; [x', fval]];
return;
end
else
% 剪枝
return;
end
end
% IsInRange.m
% 判断分支问题的解是否在上下界的范围中,若不在,剪去
function y = IsInRange(fval)
global lowerBound;
global upperBound;
if isempty(upperBound) & fval >= lowerBound
y = 1;
else if (fval >= lowerBound & fval <= upperBound)
y = 1;
else
y = 0;
end
end
% 打印输出上下界
function y = OutputLowerAndUpperBounds()
global lowerBound;
global upperBound;
disp("此时下界、上界分别为");
disp(lowerBound);
if isempty(upperBound)
disp(' 无上界')
else
disp(upperBound);
end
end