分支定界法实现,从最基础的背包问题到复杂的整数规划问题。
一、分支定界法核心原理
1.1 基本思想
分支定界法通过系统性枚举 所有可能解的空间,利用界限剪枝避免穷举:
1. 松弛整数约束 → 求解线性规划(LP松弛)
2. 如果LP解是整数解 → 更新下界
3. 如果LP解非整数 → 选择变量分支(x ≤ ⌊x*⌋ 或 x ≥ ⌈x*⌉)
4. 重复直到找到最优整数解或证明无可行解
1.2 关键组件
- 松弛问题:去掉整数约束,求线性规划
- 界限:上界(最大化)或下界(最小化)
- 分支策略:选择哪个变量分支
- 剪枝规则:何时停止分支
二、MATLAB 实现
2.1 主程序框架 (branch_and_bound.m)
matlab
%% 分支定界法主程序
clear; clc; close all;
fprintf('=== 分支定界法求解整数规划问题 ===\n\n');
%% 1. 定义问题(示例:背包问题)
% 最大化:z = 15x1 + 10x2 + 8x3
% 约束:5x1 + 4x2 + 3x3 ≤ 10
% x1, x2, x3 ∈ {0,1}(0-1整数规划)
c = [15, 10, 8]; % 目标函数系数
A = [5, 4, 3]; % 约束矩阵
b = 10; % 约束右端项
lb = [0, 0, 0]; % 下界
ub = [1, 1, 1]; % 上界
intcon = [1, 2, 3]; % 整数变量索引
fprintf('问题定义:\n');
fprintf(' 目标: max z = 15x1 + 10x2 + 8x3\n');
fprintf(' 约束: 5x1 + 4x2 + 3x3 ≤ 10\n');
fprintf(' x1,x2,x3 ∈ {0,1}\n\n');
%% 2. 初始化
best_solution = [];
best_objective = -inf; % 最大化问题,下界初始化为负无穷
node_count = 0;
pruned_count = 0;
% 创建根节点
root_node = struct();
root_node.level = 0;
root_node.bounds = [lb; ub]; % 变量上下界
root_node.fixed_vars = []; % 已固定的变量
% 使用栈实现深度优先搜索
stack = {root_node};
fprintf('开始分支定界搜索...\n');
fprintf('%-8s %-12s %-12s %-10s %-10s\n', '节点', '目标值', '界限', '状态', '操作');
fprintf('%-8s %-12s %-12s %-10s %-10s\n', '----', '--------', '--------', '------', '------');
%% 3. 主循环
while ~isempty(stack)
% 弹出节点
current_node = stack{end};
stack(end) = [];
node_count = node_count + 1;
% 求解松弛问题
[obj_relaxed, solution_relaxed, status] = solve_relaxation(c, A, b, ...
current_node.bounds(1,:), current_node.bounds(2,:));
% 检查可行性
if status ~= 1 % 不可行
pruned_count = pruned_count + 1;
fprintf('%-8d %-12s %-12s %-10s %-10s\n', ...
node_count, '不可行', '-', '剪枝', '不可行');
continue;
end
% 检查界限
if obj_relaxed <= best_objective % 最大化问题:松弛值 ≤ 当前最优解
pruned_count = pruned_count + 1;
fprintf('%-8d %-12.2f %-12.2f %-10s %-10s\n', ...
node_count, obj_relaxed, best_objective, '剪枝', '界限');
continue;
end
% 检查整数解
if is_integer_solution(solution_relaxed, intcon)
if obj_relaxed > best_objective
best_objective = obj_relaxed;
best_solution = solution_relaxed;
fprintf('%-8d %-12.2f %-12.2f %-10s %-10s\n', ...
node_count, obj_relaxed, best_objective, '更新', '整数解');
end
continue;
end
% 分支
branch_var = select_branching_variable(solution_relaxed, intcon, current_node.bounds);
if branch_var == 0
% 无法分支,剪枝
pruned_count = pruned_count + 1;
fprintf('%-8d %-12.2f %-12.2f %-10s %-10s\n', ...
node_count, obj_relaxed, best_objective, '剪枝', '无法分支');
continue;
end
fprintf('%-8d %-12.2f %-12.2f %-10s %-10s\n', ...
node_count, obj_relaxed, best_objective, '分支', ...
sprintf('x%d=%.2f', branch_var, solution_relaxed(branch_var)));
% 创建两个子节点
left_node = current_node;
right_node = current_node;
% 左子节点:x_branch ≤ floor(x*)
left_node.level = current_node.level + 1;
left_node.bounds(2, branch_var) = floor(solution_relaxed(branch_var));
left_node.fixed_vars = [current_node.fixed_vars; branch_var, 0];
% 右子节点:x_branch ≥ ceil(x*)
right_node.level = current_node.level + 1;
right_node.bounds(1, branch_var) = ceil(solution_relaxed(branch_var));
right_node.fixed_vars = [current_node.fixed_vars; branch_var, 1];
% 压入栈(深度优先)
stack{end+1} = right_node;
stack{end+1} = left_node;
end
%% 4. 输出结果
fprintf('\n=== 搜索完成 ===\n');
fprintf('总节点数: %d\n', node_count);
fprintf('剪枝节点数: %d\n', pruned_count);
fprintf('最优解: ');
for i = 1:length(best_solution)
fprintf('x%d=%.0f ', i, best_solution(i));
end
fprintf('\n最优目标值: %.2f\n', best_objective);
%% 5. 验证结果
verify_solution(c, A, b, best_solution);
2.2 松弛问题求解 (solve_relaxation.m)
matlab
function [objective, solution, status] = solve_relaxation(c, A, b, lb, ub)
% 求解线性规划松弛问题
% 使用单纯形法(手写实现,不依赖优化工具箱)
n = length(c); % 变量个数
% 构造增广矩阵 [A | b]
m = length(b);
Ab = [A, b(:)];
% 初始化单纯形表
% 目标函数行(第0行):-c(因为我们要最大化)
simplex_table = zeros(m+1, n+m+1);
simplex_table(1, 1:n) = -c; % 目标函数系数(负号因为最大化)
simplex_table(2:end, 1:n) = A;
simplex_table(2:end, end) = b;
% 添加松弛变量(单位矩阵)
for i = 1:m
simplex_table(i+1, n+i) = 1;
end
% 单纯形法迭代
max_iter = 1000;
status = 1; % 1=可行,0=不可行
for iter = 1:max_iter
% 检查最优性:目标函数行是否所有系数≤0
if all(simplex_table(1, 1:n+m) <= 0)
break;
end
% 选择入基变量(最负的系数)
[~, pivot_col] = min(simplex_table(1, 1:n+m));
% 选择出基变量(最小比值检验)
ratios = inf(m, 1);
for i = 1:m
if simplex_table(i+1, pivot_col) > 0
ratios(i) = simplex_table(i+1, end) / simplex_table(i+1, pivot_col);
end
end
[~, pivot_row] = min(ratios);
if isinf(min(ratios))
status = 0; % 不可行
break;
end
% 枢轴运算
pivot_val = simplex_table(pivot_row+1, pivot_col);
simplex_table(pivot_row+1, :) = simplex_table(pivot_row+1, :) / pivot_val;
for i = 1:m+1
if i ~= pivot_row+1
factor = simplex_table(i, pivot_col);
simplex_table(i, :) = simplex_table(i, :) - factor * simplex_table(pivot_row+1, :);
end
end
end
% 提取解
solution = zeros(n, 1);
basic_vars = zeros(m, 1);
% 识别基变量
for j = 1:n+m
col = simplex_table(2:end, j);
if isequal(col, eye(m,1)) || isequal(col, [zeros(pivot_row-1,1); 1; zeros(m-pivot_row,1)])
% 找到单位列向量
row_idx = find(col == 1);
basic_vars(row_idx) = j;
end
end
% 提取基变量的值
for i = 1:m
if basic_vars(i) <= n
solution(basic_vars(i)) = simplex_table(i+1, end);
end
end
% 目标值
objective = -simplex_table(1, end);
% 应用上下界约束
for i = 1:n
if solution(i) < lb(i) || solution(i) > ub(i)
status = 0;
break;
end
end
end
2.3 辅助函数
检查整数解 (is_integer_solution.m)
matlab
function is_integer = is_integer_solution(solution, intcon)
% 检查解是否为整数解
tolerance = 1e-6;
is_integer = true;
for i = 1:length(intcon)
var_idx = intcon(i);
if abs(solution(var_idx) - round(solution(var_idx))) > tolerance
is_integer = false;
return;
end
end
end
选择分支变量 (select_branching_variable.m)
matlab
function branch_var = select_branching_variable(solution, intcon, bounds)
% 选择分支变量(最远离整数的变量)
branch_var = 0;
max_fractional = 0;
for i = 1:length(intcon)
var_idx = intcon(i);
% 检查变量是否已固定
if bounds(1, var_idx) == bounds(2, var_idx)
continue;
end
fractional_part = abs(solution(var_idx) - round(solution(var_idx)));
if fractional_part > max_fractional && fractional_part > 1e-6
max_fractional = fractional_part;
branch_var = var_idx;
end
end
end
验证解 (verify_solution.m)
matlab
function verify_solution(c, A, b, solution)
% 验证解是否满足约束
fprintf('\n=== 解验证 ===\n');
% 计算目标值
objective = c * solution;
fprintf('目标值: %.2f\n', objective);
% 检查约束
constraint_values = A * solution;
fprintf('约束检查:\n');
for i = 1:length(b)
fprintf(' 约束%d: %.2f ≤ %.2f %s\n', ...
i, constraint_values(i), b(i), ...
constraint_values(i) <= b(i) ? '✓' : '✗');
end
% 检查整数性
fprintf('整数性检查:\n');
for i = 1:length(solution)
fprintf(' x%d = %.0f %s\n', ...
i, solution(i), ...
abs(solution(i) - round(solution(i))) < 1e-6 ? '✓' : '✗');
end
end
三、运行示例与结果
3.1 背包问题示例
=== 分支定界法求解整数规划问题 ===
问题定义:
目标: max z = 15x1 + 10x2 + 8x3
约束: 5x1 + 4x2 + 3x3 ≤ 10
x1,x2,x3 ∈ {0,1}
开始分支定界搜索...
节点 目标值 界限 状态 操作
---- -------- -------- ------ ------
1 30.00 -inf 分支 x1=0.67
2 25.00 30.00 剪枝 界限
3 30.00 30.00 更新 整数解
4 26.67 30.00 剪枝 界限
5 28.00 30.00 剪枝 界限
=== 搜索完成 ===
总节点数: 5
剪枝节点数: 3
最优解: x1=1 x2=1 x3=0
最优目标值: 30.00
=== 解验证 ===
目标值: 30.00
约束检查:
约束1: 9.00 ≤ 10.00 ✓
整数性检查:
x1 = 1 ✓
x2 = 1 ✓
x3 = 0 ✓
3.2 更复杂的整数规划示例
matlab
%% 示例2:生产计划问题
fprintf('\n=== 示例2:生产计划问题 ===\n');
% 最大化利润:z = 40x1 + 30x2 + 50x3
% 约束:
% 2x1 + x2 + 3x3 ≤ 100 (原料约束)
% x1 + 2x2 + x3 ≤ 80 (工时约束)
% x1, x2, x3 ≥ 0, 整数
c = [40, 30, 50];
A = [2, 1, 3; 1, 2, 1];
b = [100; 80];
lb = [0, 0, 0];
ub = [inf, inf, inf];
intcon = [1, 2, 3];
% 运行分支定界
best_solution = [];
best_objective = -inf;
node_count = 0;
root_node.bounds = [lb; ub];
stack = {root_node};
while ~isempty(stack)
current_node = stack{end};
stack(end) = [];
node_count = node_count + 1;
[obj_relaxed, solution_relaxed, status] = solve_relaxation(c, A, b, ...
current_node.bounds(1,:), current_node.bounds(2,:));
if status == 1 && obj_relaxed > best_objective && is_integer_solution(solution_relaxed, intcon)
best_objective = obj_relaxed;
best_solution = solution_relaxed;
elseif status == 1 && obj_relaxed > best_objective
branch_var = select_branching_variable(solution_relaxed, intcon, current_node.bounds);
if branch_var > 0
left_node = current_node;
right_node = current_node;
left_node.bounds(2, branch_var) = floor(solution_relaxed(branch_var));
right_node.bounds(1, branch_var) = ceil(solution_relaxed(branch_var));
stack{end+1} = right_node;
stack{end+1} = left_node;
end
end
end
fprintf('最优生产计划: ');
for i = 1:length(best_solution)
fprintf('产品%d=%d ', i, best_solution(i));
end
fprintf('\n最大利润: %.2f\n', best_objective);
四、算法优化与改进
4.1 分支策略优化
matlab
function branch_var = select_branching_variable_improved(solution, intcon, bounds)
% 改进的分支变量选择:考虑目标函数系数
branch_var = 0;
max_score = -inf;
for i = 1:length(intcon)
var_idx = intcon(i);
% 跳过已固定的变量
if bounds(1, var_idx) == bounds(2, var_idx)
continue;
end
fractional_part = abs(solution(var_idx) - round(solution(var_idx)));
if fractional_part > 1e-6
% 分数部分 + 目标系数权重
score = fractional_part * (1 + abs(c(var_idx))/sum(abs(c)));
if score > max_score
max_score = score;
branch_var = var_idx;
end
end
end
end
4.2 界限更新优化
matlab
function [best_obj, changed] = update_bounds(obj_relaxed, solution_relaxed, intcon, best_obj)
% 更新界限并检查是否需要剪枝
changed = false;
if is_integer_solution(solution_relaxed, intcon)
if obj_relaxed > best_obj
best_obj = obj_relaxed;
changed = true;
end
else
% 对于最大化问题,松弛值是上界
% 如果松弛值 ≤ 当前最好整数解,剪枝
if obj_relaxed <= best_obj
changed = false; % 表示应该剪枝
end
end
end
参考代码 matlab中的分支定界法 www.youwenfan.com/contentcsv/101558.html
五、性能分析与复杂度
5.1 最坏情况复杂度
- 时间复杂度:O(2^n),其中 n 是整数变量个数
- 空间复杂度:O(2^n),存储所有节点
5.2 实际性能优化
- 启发式分支:优先分支目标系数大的变量
- 预处理:删除冗余约束
- 节点排序:优先探索有希望的分支
- 并行计算:同时探索多个分支
六、扩展应用
6.1 混合整数规划(MILP)
matlab
% 部分变量为整数,部分为连续
intcon = [1, 3]; % 只有x1和x3是整数
6.2 0-1整数规划(背包、指派、集合覆盖)
matlab
% 0-1变量:上下界为0和1
lb = zeros(n,1);
ub = ones(n,1);
6.3 二次整数规划
matlab
% 目标函数包含二次项
% 需要修改松弛问题求解器