大M法(Big M Method)

前面一篇讲的单纯形方法的实现,但程序输入的必须是已经有初始基本可行解的单纯形表。

但实际问题中很少有现成的基本可行解,比如以下这个问题:

min f(x) = –3x1 +x2 + x3

s.t. x1 – 2x2 + x3 + x4=11

      -4x1 + x2 + 2x3 - x5=3

      -2x1+x3=1

      xj>=0 , j=1,2,3,4,5

写成单纯形表就是

 x1x2x3x4x5b
f3-1-1000
 1-211011
 -4120-13
 -201001

很难找到秩为3的基阵,更不用说直接出现3阶单位阵了。在实际问题中,尤其是约束条件变多时,找到基阵甚至是判定A是否满秩都十分困难,因此在程序中引入大M法(Big M Method)来获得初始的基本可行解,这样我们能处理的问题就更加多样化了。

上篇已经说过,对于m*n的矩阵A来说,找到一个m*m 的满秩方阵就能得到基本可行解,但是在这么多列向量中怎样挑出m个线性无关的向量来组成一个满秩方阵呢?如果找起来麻烦的话,不如直接添加一个m阶单位阵来的方便!

大M法

大M法又称惩罚法,它是在目标函数中添加m个人工变量M*x(M是一个任意大的正数),同时在A矩阵中添加一个m阶单位矩阵。

image

这样一来新的A矩阵中就有了一个m*m满秩方阵,满足单纯形法求解的初始要求,但是若要得到最小值f(x),新添加的人工变量的值必然是0的,因为M可以是很大的数,如果Xn+1不为0,f(x)可能会很大,如果无法做到令人工变量取0值,那么原问题就无可行解。

需要注意的是,添加完人工变量之后,人工变量构成一组可行解的基变量,但单纯形初始矩阵要求基变量对应的检验数为0,故需要做行变换把基变量对应的检验数置0。

例如,本文开始引入的问题经过添加人工变量后变为

 x1x2x3x4x5x6x7x8b
f3-1-100-M-M-M0
x61-211010011
x7-4120-10103
x8-201000011

再进行行变换把基变量x6,x7,x8对应的检验数置0,得到:

 x1x2x3x4x5x6x7x8b
f3-5M-1-M-1+4M000000
x61-211010011
x7-4120-10103
x8-201000011

进行完这步之后,就回到了单纯形法求解的基本问题,利用原来的算法继续计算就好了。

Matlab实现

BigM.m

function [ x,y ] = BigM( f,A,b )
%输入f是检验数的数组,1*n维
%输入A是约束矩阵, m*n维
%输入b是约束向量, 1*m维
%输出x是解向量
%输出y是最优解
%判断输入维数是否相符
%做初始单纯形表,加入M变量
[n,m]=size(A);%n行m列
M=10000;
S=[f -1*M*ones(1,n) 0;
   A   eye(n)       b'];
format rat %将结果以分数表示
[n,m]=size(S);
%将人工变量的检验数置零
for k=1:n-1
    S(1,:)=S(1,:)+S(k+1,:)*M;   
end
%判断检验数 r<=0
r=find(S(1,1:m-1)>0);
len=length(r);
flag=0;
%有大于0的检验数则进入循环
while(len)
    %检查非负检验数所对列向量元素是否都小于等于0
    for k=1:length(r)
        d=find(S(:,r(k))>0);
        if(length(d)+1==2)
        error('无最优解!!!')  
        %break;
        end
    end
    %找到检验数中最大值
    [Rk,j]=max(S(1,1:m-1));
    %最大值所在列比值为正数且最小值br/a_rk
    br=S(2:n,m)./S(2:n,j);
    %把比值中的负数都变无穷
    for p=1:length(br)
        if(br(p)<0)br(p)=Inf;
        end
    end
    [h,i]=min(br);%列向量比值最小值
    % i+1为转轴元行号(在S中),j为转轴元列号
    i=i+1;
    %进行换基,转轴元置1
    S(i,:)=S(i,:)./S(i,j);
    %转轴元所在列其他元素都置0
    for k=1:n
        if(k~=i)
            S(k,:)=S(k,:)-S(i,:)*S(k,j);
        end   
    end
    %判断检验数 r<=0
    r=find(S(1,1:m-1)>0);
    len=length(r);
%     %调试用,控制循环步数
%     if(len>0)flag=flag+1;end
%     if(flag==2)break;end
%     S
end
    %检验数全部非正,找到最优解
    %非基变量置0
    x=zeros(1,m-1);
    for i=1:m-1
        %找到基变量
        j=find(S(:,i)==1);%每列中1的个数
        k=find(S(:,i)==0);%每列中0的个数
        if((length(j)+1==2)&&(length(k)+1==n))
            %i为基本元列号,j是行号
            x(i)=S(j,m);
        end
    end
    y=S(1,m);%最优解
    S
end

测试程序:

f=[3 -1 -1 0 0];
A=[1 -2 1 1  0;
  -4  1 2 0 -1;
  -2  0 1 0  0 ];
b=[11 3 1 ];
[x,y]=BigM(f,A,b)
f=[5 2 3 -1];
A=[1 2 3 0 ;
   2 1 5 0 ;
   1 2 4 1 ];
b=[15 20 26];
[x,y]=BigM(f,A,b)
f=[5 10 0 0 0 ];
A=[1/14 1/7 1 0 0;
   1/7 1/12 0 1 0;
   1    1   0 0 1 ];
b=[1 1 8];
[x,y]=BigM(f,A,b)
[x,y]=Simplex(f,A,b)

计算结果:

image

imageimage

转载于:https://www.cnblogs.com/oucsheep/p/3422160.html

### 带有 Big M 的线性规划问题求解 Big M 是一种处理人工变量的方,适用于初始基不可行的情况。通过引入一个极的常数 \(M\) 来惩罚人工变量的存在,从而引导算找到最优解并使人工变量退出基底。 以下是基于 Python 的实现方式: #### 使用 `numpy` 和手动实现 Big M 可以通过矩阵运算来模拟单纯形表的过程,并结合 Big M 方完成求解。以下是一个简单的实现示例[^2]: ```python import numpy as np class BigMMethod: def __init__(self, c, A, b): self.c = np.array(c) self.A = np.array(A) self.b = np.array(b) def add_artificial_variables(self): m, n = self.A.shape I = np.eye(m) artificial_vars = [] # 添加人工变量到系数矩阵和目标函数中 new_A = np.hstack((self.A, I)) new_c = list(self.c) + [M]*m return new_A, new_c def initialize_tableau(self, new_A, new_c): tableau = np.zeros((new_A.shape[0]+1, new_A.shape[1]+1)) tableau[:-1,:-1] = new_A tableau[-1,:-1] = -np.array(new_c) tableau[:-1,-1] = self.b return tableau def solve(self): global M # 定义全局变量 M 表示极值 M = float(1e9) new_A, new_c = self.add_artificial_variables() tableau = self.initialize_tableau(new_A, new_c) iteration_count = 0 while any(tableau[-1][:-1] < 0): # 寻找进入基的列 entering_col_idx = np.argmin(tableau[-1][:-1]) if all(tableau[:-1,entering_col_idx] <= 0): # 检查是否存在无界解 raise ValueError("Unbounded solution detected.") leaving_row_idx = None min_ratio = float('inf') for i in range(len(self.b)): if tableau[i, entering_col_idx] > 0: ratio = tableau[i][-1] / tableau[i][entering_col_idx] if ratio < min_ratio and ratio >= 0: min_ratio = ratio leaving_row_idx = i if leaving_row_idx is None: # 如果找不到离开基的行,则无继续迭代 break # 进行高斯消元更新表格 pivot_element = tableau[leaving_row_idx, entering_col_idx] tableau[leaving_row_idx,:] /= pivot_element for row in range(tableau.shape[0]): if row != leaving_row_idx: multiplier = tableau[row, entering_col_idx] tableau[row,:] -= multiplier * tableau[leaving_row_idx,:] iteration_count += 1 if iteration_count >= 100: # 防止死循环 raise RecursionError('This linear programming problem is not convergent') optimal_value = tableau[-1][-1] variables_values = tableau[:, -1][:len(self.c)] return {"optimal value": optimal_value, "variables values": variables_values} # 测试数据 c = [-3, -4, 0, 0] # 目标函数系数 A = [[1, 2, 1, 0], [3, 2, 0, 1]] # 不等式约束左侧 b = [8, 12] # 不等式约束右侧 solver = BigMMethod(c, A, b) result = solver.solve() print(result) ``` 上述代码实现了带有人工变量的 Big M ,并能够自动检测无界解或不可行解的情况。 --- #### 利用现有库(如 SciPy) 对于更复杂的场景,可以直接调用现有的科学计算库,例如 SciPy 提供了优化模块 `optimize.minimize` 或者专门针对线性规划的接口 `linprog`[^6]。虽然这些工具并未显式支持 Big M ,但它们内部已经集成了高效的求解器(如单纯形或内点),因此通常无需手动实现 Big M 。 下面展示了一个简单例子: ```python from scipy.optimize import linprog # 输入参数 c = [-3, -4] # 负号表示最化问题转换为最小化问题 A_ub = [[1, 2], [3, 2]] b_ub = [8, 12] bounds = [(0, None), (0, None)] # 变量非负约束 res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method="highs") if res.success: print(f"Optimal Value: {res.fun}") print(f"Variables Values: {res.x}") else: print(res.message) ``` 此方更适合实际工程需求,因为它更加高效且易于维护[^6]。 --- ### 性能与局限性 尽管单纯形多数情况下表现良好,但在最坏情形下仍可能退化至指数级复杂度[^3]。相比之下,现代算如椭球或内点提供了更强的理论保障,但对于小型问题而言,单纯形仍然是首选方案之一。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值