MATLAB 分支定界法(Branch and Bound)实现

分支定界法实现,从最基础的背包问题到复杂的整数规划问题。


一、分支定界法核心原理

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 实际性能优化

  1. 启发式分支:优先分支目标系数大的变量
  2. 预处理:删除冗余约束
  3. 节点排序:优先探索有希望的分支
  4. 并行计算:同时探索多个分支

六、扩展应用

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 复制代码
% 目标函数包含二次项
% 需要修改松弛问题求解器
相关推荐
basketball6161 小时前
设计模式入门:2. 工厂模式详解 C++实现
开发语言·c++·设计模式
Lumbrologist1 小时前
【C++】零基础入门 · 第 16 节:智能指针
开发语言·c++
学会去珍惜1 小时前
c语言编程 C语言入门 c语言(C语言程序设计教程 c语言视频教程 c语言零基础
c语言·开发语言
AI 编程助手GPT1 小时前
ChatGPT 新手入门与实战操作指南
开发语言·人工智能·git·python·chatgpt
Brilliantwxx1 小时前
【C++】 红黑树封装 STL set/map 超详细解析
开发语言·c++
程序大视界1 小时前
【C++ 从基础到项目实战】C++(八):运算符重载——让你的类用起来像内置类型
开发语言·c++·cpp
原创小甜甜1 小时前
OOM 排查复盘:Hutool 序列化 Request 导致 Java Heap Space
java·开发语言·python
萨小耶1 小时前
[Java学习日记10】聊聊checked exception和runtime exception
java·开发语言·学习
不会C语言的男孩1 小时前
C++ Primer 第6章:函数
开发语言·c++