【运筹学】单纯形法(两阶段法)Python实现(第二版)

1 单纯形法(两阶段法)--Python

        两阶段法主要是用于初始基可行解难以寻找的情况;

1.1 创建第一阶段单纯形表,添加人工变量

import numpy as np
import sys
def create_first_tableau(A,b,num_constraints,num_variables):
    basic_vars = np.arange(num_variables, num_variables + num_constraints)
    non_basic_vars = np.arange(num_variables)
    # np.hstack水平组合向量或矩阵;array.reshape(-1,1)将b转化为n行1列的矩阵;
    tableau=np.hstack((A, b.reshape(-1,1) ))
    for i in range(num_constraints): # 保持b列始终非负
        if tableau[i,-1]<0:
            tableau[i,:]*=-1
    basic_matrix=np.eye(num_constraints)
    tableau=np.insert(tableau,num_variables,basic_matrix,axis=1)
    c_row=np.zeros(num_constraints+num_variables+1)# 构建系数行
    for i in range(num_constraints):
        c_row-=tableau[i,:]
    c_row[basic_vars]=0
    tableau=np.vstack((tableau,c_row))
    print("第一阶段:\n")
    print("Iteration 0:\n",tableau)
    print("Basic Variables:", basic_vars+1)
    print("Non-Basic Variables:", non_basic_vars+1)
    return tableau,basic_vars,non_basic_vars

1.2 创建第二阶段单纯形表,消除人工变量

def create_second_tableau(tableau,c,num_constraints,num_variables,basic_vars,non_basic_vars):
    artificial_vari=np.arange(num_variables, num_variables + num_constraints)
    non_basic_vars=non_basic_vars[~np.isin(non_basic_vars,artificial_vari)]
    c_row=np.hstack((c,np.zeros(num_constraints+1))) # 构建系数行
    tableau[-1,:]=c_row
    new_tableau=[i for i in range(num_variables)]
    new_tableau.append(-1) # 消除人工变量列
    tableau=tableau[:,new_tableau]
    for i,arg in enumerate(basic_vars):
        tableau[-1,:]+=-tableau[-1,arg]*tableau[i,:]
    print('\n第二阶段:\n')
    print("Iteration 0:\n",tableau)
    print("Basic Variables:", basic_vars+1)
    print("Non-Basic Variables:", non_basic_vars+1)
    return tableau,basic_vars,non_basic_vars

1.3 定义换基(迭代)过程

# 首先判断目标函数系数向量中非基变量的值是否非负,若全是非负数,则达到最优;若存在负数,则取最小值对应的变量作为入基变量;
# 原理是利用目标函数系数向量*变化量向量(改进方向/非基变量列),观察是否减小;
# 由于目标函数系数向量跟着变化(单纯形字典),用非基变量表示目标函数,而换基时是固定一个非基变量,变化一个非基变量,因此可以只看目标函数系数即可
def pivot(tableau,basic_vars,non_basic_vars):
    tableau_N=tableau[-1,non_basic_vars]
    arg_min_N=np.argmin(tableau_N)
    entering_vari_col=non_basic_vars[arg_min_N]
    if tableau[-1,entering_vari_col]>=0:
        print('当前解为最优解') 
        return False
    else:
        print(f'入基变量为x{entering_vari_col+1};',end='')
        step=np.where(tableau[:-1,entering_vari_col]>0)[0]
        if len(step)==0:
            print('\n此LP问题无界')
            sys.exit()
        else:
            #最小比值法确定步长和出基变量;存在疑问:基变量的变化量会为0吗?如果变化为0,则约束没变
            ratios=tableau[:-1,-1]/tableau[:-1,entering_vari_col]# b列是基变量的取值,始终非负
            ratios=np.where(ratios<0,np.inf,ratios)
            leaving_vari_row=np.argmin(ratios) #需要取正值最小值的索引
            print(f'出基变量为x{basic_vars[leaving_vari_row]+1};')
            tableau[leaving_vari_row,:]/=tableau[leaving_vari_row,entering_vari_col]
            for i in range(len(tableau)):
                if i!=leaving_vari_row:
                    tableau[i,:]-=tableau[i,entering_vari_col]*tableau[leaving_vari_row,:]
            print(tableau)
            convert=basic_vars[leaving_vari_row]
            basic_vars[leaving_vari_row]=non_basic_vars[arg_min_N]
            non_basic_vars[arg_min_N]=convert
            print("Basic Variables:", basic_vars+1)
            print("Non-Basic Variables:", non_basic_vars+1)
            return True

1.4 提取目标函数值和解的情况

def extract_optimal(tableau,basic_vars,non_basic_vars):
    optimal_value = -tableau[-1, -1]  # tableau[-1, -1]是目标函数最小值的负数
    optimal_x=np.zeros(len(tableau[0])-1)
    for i,arg in enumerate(non_basic_vars):
        optimal_x[arg]=0
    for i,arg in enumerate(basic_vars):
        optimal_x[arg]=tableau[i,-1]
    print("目标函数最小值:", optimal_value)
    print("解的情况:",end='')
    for i,val in enumerate(optimal_x):
        print(f'x{i+1}={val}',end='; ')
    return optimal_x,optimal_value

2 输入参数等

# 输入的参数一定是标准化后的线性规划问题的参数
c = np.array([-2,-1,-9,0,0])  # 目标函数的系数
A = np.array([[1,1,0,1,0],
              [-2,0,1,0,0],
              [0,3,5,0,-1]])  # 约束矩阵
b = np.array([18, -2, 15])  # 约束的右端项
num_constraints,num_variables=A.shape

2.1 求解第一阶段

tableau,basic_vars,non_basic_vars=create_first_tableau(A,b,num_constraints,num_variables)
iteration=1
while True:
    print(f"\nIteration {iteration}:",end='')
    iteration+=1
    if pivot(tableau,basic_vars,non_basic_vars)==False:
        break
    elif iteration==100: # 防止进入循环
        print('\n此问题无可行解')
        sys.exit()
optimal_x1,optimal_value1=extract_optimal(tableau,basic_vars,non_basic_vars)
if abs(optimal_value1)<=0.000001: # 该例题中目标函数值趋近于0,不知道问题所在
    print("\n人工变量之和为0,原问题存在可行解,进行第二阶段计算")
else:
    print("\n人工变量之和不为0,原问题不存在可行解,停止计算")
    sys.exit()

2.2 求解第二阶段

tableau,basic_vars,non_basic_vars=create_second_tableau(tableau,c,num_constraints,num_variables,basic_vars,non_basic_vars)
iteration=1
while True:
    print(f"\nIteration {iteration}:",end='')
    iteration+=1
    if pivot(tableau,basic_vars,non_basic_vars)==False:
        break
    elif iteration==100:
        print('\n此问题无可行解')
        sys.exit()
optimal_x2,optimal_value2=extract_optimal(tableau,basic_vars,non_basic_vars)

3 例题及运行结果

3.1 例题及标准化过程

3.2 算法运行结果

3.3 验证结果(Lingo)

4 修改说明 

        昨天的代码中没有注意到消除人工变量后非基变量索引的变化,作此修改non_basic_vars=non_basic_vars[~np.isin(non_basic_vars,artificial_vari)],并规范了更多代码运行流程;仍然存在的问题:1.没有将线性规划问题标准化的过程,需要读者先将问题进行标准化;2.仍未考虑退化解以及循环的情况;

5 关于作者

        本文旨在记录作者学习运筹学过程中复现经典算法的一步步过程,可能存在算法流程不完善、表述不清楚的情况,请不吝您的宝贵意见,作者会持续学习和完善!

### 使用MATLAB实现两阶段法求解线性规划单纯形问题 #### 定义与概述 线性规划是一种优化技术,在给定一系列线性不等式约束下寻找线性目标函数的最大值或最小值。当初始基本可行解不易找到时,通常采用两阶段法来解决这个问题[^3]。 #### 两阶段法简介 两阶段法分为两个主要部分: - **第一阶段**:引入人工变量构建辅助模型,目的是通过调整这些新加入的人工变量使得原问题有一个初始的基本可行解。 - **第二阶段**:一旦获得了初始基本可行解,则移除所有的人工变量并继续按照标准的单纯形算法迭代直到最优解被发现。 #### MATLAB中的具体实施步骤 ##### 第一阶段:创建辅助LP问题 为了启动这个过程,需要先建立一个新的线性规划问题作为辅助问题。该问题是基于原始问题而来的,但是增加了一个人工变量向量`w`用于表示松弛变量不足的部分。此时的目标是最小化这些人造变量之和以确保能够达到一个有效的起始点。 ```matlab % 假设 A 是系数矩阵 b 是右侧常数项 c 是成本向量 f_phase_1 = [zeros(size(A,2),1); ones(length(b),1)]; % 新的成本向量 Aeq_phase_1 = [A eye(length(b))]; % 扩展后的等式左侧表达式 beq_phase_1 = b; lb_phase_1 = zeros(size(f_phase_1)); [x_phase_1,fval_phase_1,exitflag_phase_1,output_lambda_phase_1] = linprog(f_phase_1,[],[],Aeq_phase_1,beq_phase_1,lb_phase_1); if exitflag_phase_1 <= 0 || fval_phase_1 > eps disp('The original problem does not have a feasible solution.'); else % 继续执行下一阶段... end ``` ##### 第二阶段:去除人工变量并应用单纯形方法 如果成功找到了至少一个基础可行解(即 `fval_phase_1 ≈ 0`),那么就可以进入下一个阶段——去掉之前添加的所有人工变量,并利用得到的基础解重新设置新的线性规划问题来进行最终的优化计算。 ```matlab active_vars_idx = find(x_phase_1(1:end-length(b))); % 获取非零基变量索引 new_A = A(:, active_vars_idx); % 更新系数矩阵只保留活动列 c_new = c(active_vars_idx); % 对应更新成本向量 [x_optimal,fval_optimal,exitflag_optimal] = linprog(c_new,[],[],new_A,b, lb_phase_1(active_vars_idx)); fprintf('Optimization completed with objective value %.4f\n', fval_optimal); disp(['Final values of decision variables:', num2str(x_optimal')]); ``` 上述代码片段展示了如何在MATLAB中使用内置函数`linprog()`完成整个流程。需要注意的是实际操作过程中还需要考虑更多细节比如处理退化解等情况,这里仅提供了一个简化本供理解概念[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值