Bootstrap

运筹学之单纯形法(超详细讲解 + matlab可运行代码实现)

目录

1.单纯形法的定义

2.单纯形法的基本原理

2.1 核心步骤

2.2 步骤拆解

2.2.1 如何确定初始的基可行解?

2.2.2 如何进行解的最优性判别?

2.2.3 如何寻找改进的基可行解?

3.用matlab实现单纯形法

3.1 单纯形法算法代码

3.2 测试案例


1.单纯形法的定义

单纯形法是一种基于几何顶点遍历的线性规划求解算法,其核心逻辑可概括为:

  1. 顶点择优原理
    • 线性规划的可行域构成凸多面体,最优解必出现在顶点(基可行解)上。
    • 通过有限步迭代,从一个顶点沿目标函数改进方向移动到相邻顶点。
  2. 方向选择机制
    • 利用**检验数(Reduced Cost)**判断当前解的改进潜力。
    • 若存在正检验数(最大化问题)或负检验数(最小化问题),则目标函数可继续优化。
  3. 终止条件
    • 所有检验数均不满足优化方向时达到最优解。
    • 若发现无界方向(存在无限改进空间),则问题无界。

2.单纯形法的基本原理

2.1 核心步骤

第一步:将线性规划的标准型化成线性规划的规范型,来获得一个初始可行解。

第二步:对初始基可行解最优性判别,若最优,则停止;否则转下一步。

第三步:从初始基可行解向相邻的基可行解转换,使得目标值有所改善,重复第二步和第三步直至找到最优解。

2.2 步骤拆解

        为更清楚地了解并操作单纯形法,必须要解决三个问题:

2.2.1 如何确定初始的基可行解?

        首先我们先了解四个基本概念

        规范型:约束条件矩阵A中有一个单位矩阵I

        基:约束系数矩阵的A的秩为m,若A中的Bm*m可逆矩阵,则称B为线性规划问题的一个基。

        基解对于基B是对应于基B的决策变量基变量),系数矩阵A中的其它列向量对应的决策变量称为非基变量。如果令非基变量为0,则称基解

        基可行解非负条件的基解称为基可行解

        认识了四个基本概念后,我们先将线性规划标准型为规范型,其中,系数矩阵中含有一个单位矩阵 I ,不妨设单位矩阵为:

        简单示例如下:

        找到初始可行基令非基变量取值为0,便得到一组基可行解

        上述示例的基可行解如下:

2.2.2 如何进行解的最优性判别?

        首先我们先了解一个概念:

        检验数表示将非基变量  增加一个单位时,目标函数值的变化率。

        其计算公式为:

  •  :非基变量 ​ 在目标函数中的系数
  •  ​:基变量在目标函数中的系数向量
  •  :基矩阵的逆矩阵
  •  :非基变量  在约束中的系数列向量

        实例演算:

        步骤1:计算初始检验数

        实例演算:

        步骤2:判别最优性

        判别规则

  •  最大化问题(Max):若所有非基变量的  ≤ 0,当前解为最优解。
  •  最小化问题(Min):若所有非基变量的  ≥ 0,当前解为最优解。

        实例演算:

        所有非基变量的检验数均  (最大化问题),说明当前解 Z = 0 非最优,需迭代改进。

2.2.3 如何寻找改进的基可行解?

        首先我们了解两个概念:

        出基变量:从当前基变量中被移除的变量,其离基是为保证新解的可行性(非负约束)

        入基变量:从非基变量中被选中加入基的变量,其引入将直接改进目标函数值

        步骤一: 选择入基变量--选择最大正检验数对应的变量

        实例演算:

        步骤二:选择出基变量,确保新解满足非负约束,通过 最小比值测试(Minimum Ratio Test)选择出基变量,其公式为:

          : 当前基变量取值

         : 入基变量  在约束中的系数

        实例演算:

        步骤三:基变换与解更新

        操作

  1. 构造新基矩阵:将入基变量  的列替换出基变量对应的列
  2. 高斯消元:通过行变换将新基变量对应的列转换为单位向量
  3. 更新解:令非基变量为0,直接读取基变量取值

        实例演算:

        第一次操作完以上三步后,重复第二步和第三步直至找到最优解。

3.用matlab实现单纯形法

3.1 单纯形法算法代码

function [xm, fm, exitflag, iterations] = enhanced_simplex(c, A, b, max_iter)
% ENHANCED_SIMPLEX 改进版单纯形法 
% 功能:解决标准形线性规划问题 min c'x s.t. Ax = b, x >= 0 
% 新增特性:两阶段法、LU分解、Bland规则 
 
%% ========== 输入参数校验与初始化 ==========
if nargin == 0  % 测试案例 
    c = [-3; -2; 0; 0];
    A = [2, 1, 1, 0; 1, 2, 0, 1];
    b = [4; 3];
    max_iter = 500;
elseif nargin < 4 
    max_iter = 500;
end 
 
[m, n] = size(A);
exitflag = -1; xm = []; fm = 0; iterations = 0;
 
% 维度一致性检查 
if length(b) ~= m || length(c) ~= n 
    error('输入维度不匹配: A是%dx%d, b是%dx1, c是%dx1', m,n,length(b),length(c));
end 
%% ========== 第一阶段:可行性检查 ==========
% 构造人工变量 
phase1_A = [A, eye(m)];
phase1_c = [zeros(n,1); ones(m,1)];
artificial_basis = (n+1 : n+m)';  % 必须转为列向量 
% 调用核心算法 
[x_phase1, ~, exitflag_phase1] = core_simplex(phase1_c, phase1_A, b, artificial_basis, max_iter);
% 判断可行性 
if exitflag_phase1 ~= 1 || any(abs(x_phase1(n+1:end)) > 1e-6)
    disp('无可行解');
    return;
end 
 
%% ========== 第二阶段:优化原目标 ==========
basis = find(x_phase1(1:n) > 1e-6);  % 提取有效基 
if length(basis) ~= m 
    error('基变量数量不足,可能遇到退化问题');
end 
[xm, fm, exitflag, iterations] = core_simplex(c, A, b, basis, max_iter);
end  % 主函数结束 
 
%% ========== 核心算法子函数 ==========
function [x_opt, f_opt, exitflag, iter] = core_simplex(c, A, b, basis, max_iter)
% CORE_SIMPLEX 核心单纯形算法实现 
%% 初始化 
[m, n] = size(A);
tol = 1e-10;
x_opt = zeros(n,1);
exitflag = -1;
iter = 0;
% 初始基校验 
A_b = A(:,basis);
if rank(A_b) < m 
    error('初始基矩阵秩不足');
end 
[L, U, P] = lu(A_b);  % LU分解 
%% 主循环 
for iter = 1:max_iter 
    % 计算检验数 
    lambda = L' \ (U' \ c(basis));
    reduced_cost = c' - lambda' * A;
    % 最优性检查 
    if all(reduced_cost >= -tol)
        x_b = U \ (L \ (P*b));
        x_opt(basis) = x_b;
        f_opt = c' * x_opt;
        exitflag = 1;
        return;
    end 
    % Bland规则选择入基变量 
    s = find(reduced_cost < -tol, 1, 'first');
    % 计算方向向量 
    As = A(:,s);
    direction = U \ (L \ (P*As));
    % 无界判断 
    if all(direction <= tol)
        exitflag = 0;
        return;
    end 
    % 计算离基变量 
    x_b = U \ (L \ (P*b));
    theta = x_b ./ direction;
    theta(direction <= tol) = Inf;
    [~, q] = min(theta);
    % 更新基变量 
    basis(q) = s;
    % 更新LU分解 
    A_b = A(:,basis);
    [L, U, P] = lu(A_b);
end 

exitflag = -1;  % 超过迭代限制 
end  % 子函数结束 

3.2 测试案例

% ========== 测试案例执行 ==========
c = [-3; -2; 0; 0];
A = [2, 1, 1, 0; 
     1, 2, 0, 1];
b = [4; 3];
[x, fval, flag] = enhanced_simplex(c, A, b);
 
% ========== 输出优化 ==========
disp('════════════════ 单纯形法求解结果 ════════════════');
fprintf('算法状态: %s\n', getExitStatus(flag));  % 状态解释 
fprintf('迭代次数: %d\n', length(x));          % 显示实际迭代次数 
disp('--------------------------------------------');
 
% 解向量分列显示(含变量类型标注)
var_types = {'x₁', 'x₂', 's₁', 's₂'};  % s表示松弛变量 
disp('[最优解]');
for i = 1:length(x)
    fprintf('%s = %8.4f\n', var_types{i}, x(i));
end 
 
fprintf('\n目标函数值: %.4f → 原始问题等效为最大利润: %.2f\n',...
        fval, -fval);  % 转换为最大化问题解释 
disp('══════════════════════════════════════════════');
 
% ========== 辅助函数:状态码转文字 ==========
function status = getExitStatus(flag)
    switch flag 
        case 1,  status = '找到最优解 ✅';
        case 0,  status = '问题无界解 ⚠️ (目标值趋向无穷)';
        case -1, status = '无可行解 ❌';
        otherwise, status = '未知状态';
    end 
end 

输出的结果:

════════════════ 单纯形法求解结果 ════════════════
算法状态: 找到最优解 
迭代次数: 4
--------------------------------------------
[最优解]
x₁ =   1.6667
x₂ =   0.6667
s₁ =   0.0000
s₂ =   0.0000

目标函数值: -6.3333 → 原始问题等效为最大利润: 6.33
══════════════════════════════════════════════

如有问题,可私信或在评论区交流,定即时回复,感谢支持!

;