对偶单纯形法算法精要

单纯形法是线性规划中最经典且广泛应用的求解方法,通过在可行解的边界上移动,逐步逼近最优解。它从一个初始基本可行解开始,不断优化目标函数值,直到找到最优解。对偶单纯形法则是单纯形法的一种变形,尤其适用于特定类型的线性规划问题。不同于标准的单纯形法,对偶单纯形法从一个对偶可行但原始不可行的初始解出发,通过逐步改善解的可行性和最优性,最终找到最优解。对偶单纯形法在快速调整和重新优化方面表现出色,特别是在处理新的约束或变量被添加到现有模型中的情况时,这使得它在实际应用中具有独特的优势。

单纯形法比较 对偶单纯形流程图

一、对偶单纯形算法

‌对偶单纯形法是一种用于求解线性规划问题的数学方法,由美国数学家‌C.莱姆基在1954年提出。这种方法基于‌对偶理论,通过迭代过程逐步搜索原始问题的最优解。在迭代过程中,始终保持基解的对偶可行性,使不可行性逐步消失。‌对偶单纯形法的基本思想是:从原规划的一个对偶可行解出发;然后检验原规划的基本解是否可行,即是否有负的分量,如果有小于零的分量,则进行迭代,求另一个基本解,此基本解对应着另一个对偶可行解(检验数非正)。如果得到的基本解的分量皆非负,则该基本解为最优解。也就是说,对偶单纯形法在迭代过程中始终保持对偶解的可行性(即检验数非正),使原规划的基本解由不可行逐步变为可行,当同时得到对偶规划与原规划的可行解时,便得到原规划的最优解。

1.1 对偶问题算法步骤

a)根据线性规划典式形式,建立初始对偶单纯形表。此表对应原规划的一个基本解。要求检验数行各元素一定非正,原规划的基本解允许有小于零的分量;
b)若基本解的所有分量皆非负,则得到原规划的最优解,停止计算;若基本解中有小于零的分量bj<0,并且bj所在行各系数aij0,则原规划没有可行解,停止计算;若bj<0,并且存在aij<0,则确定xi为出基变量,并计算

θ=min{σjaij|aij<0}=σkaik

确定xk为进基变量。若有多个bj<0,则选择最小的进行分析计算;
c)以aik为中心元素,按照与单纯形类似的方法,在表中进行迭代计算,返回步骤 b)。

1.2 练习

例1:用对偶单纯形法求解下面线性规划

minw=2x1+3x2+4x3s.t. {x1+2x2+x332x1x2+3x34x1,x2,x30

max w=2x13x24x3s.t. {x12x2x3+x4=32x1+x23x3+x5=4x1,,x50

得原问题的最优解为x1=11/5x2=2/5x3=0;最优值为w=28/5

二、例题

例2:利用对偶单纯形法求解线性规划问题:

{minf(x)=4x1+12x2+18x3s.t. x1+3x332x2+2x35x1,x2,x30

解:先标准化:

{maxf(x)=4x112x218x3s.t.x1+3x3x4=32x2+2x3x5=5x1,x2,x3,x4,x50

为了将 B=(p4,p5) 作为初始基,对约束条件两边同乘 1

{x13x3+x4=32x22x3+x5=5

CTcBTBA=(4,12,18,0,0)T

对应的初始单纯形表如下:

变量 x1 x2 x3 x4 x5
f 0 -4 -12 -18 0 0
x4 -3 -1 0 -3 1 0
x5 -5 0 -2 -2 0 1

当前解 x=(0,0,0,3,5)T 为非可行解,但是它是对偶可行解。

因为:

min{3,5}=5,r=2,xj2=x5 为离基变量

b0sbrs=min{b0jbrjbrj<0}=min{122,182}=6=b02b22,s=2

因此,进基变量 xs=x2,且 b22=2 为主元。

经旋转变换后,有:

变量 x1 x2 x3 x4 x5
f 30 -4 0 -6 0 -6
x4 -3 -1 0 -3 1 0
x2 5/2 0 1 1 0 -1/2

此时 bi00,(i=1,...,m) 不成立,仍为不可行解。

因为:

br0=min{bi0bi0<0,i=1,...,m}=min{3}=3,x4 为离基变量,r=1

b0sbrs=min{b0jbrjbrj<0}=min{41,63}=2=b03br3,s=3

因此,进基变量 xs=x3,且 b13=3 为主元。

经旋转变换后,有:

变量 x1 x2 x3 x4 x5
f 36 -2 0 0 -2 -6
x3 1 1/3 0 1 -1/3 0
x2 3/2 -1/3 1 0 1/3 -1/2

此时 bi00,(i=1,...,m) 成立,是可行解,算法终止。

最优解和最优值:

x¯=(0,32,1)Tf(x¯)=36

三、对偶单纯形的Python求解

例3:假设食物的各种营养成分、价格如下表:

Food Energy (能量) Protein (蛋白质) Calcium (钙) Price
Oatmeal (燕麦) 110 4 2 3
Whole milk (全奶) 160 8 285 9
Cherry pie (樱桃派) 420 4 22 20
Pork with beans (猪肉) 260 14 80 19

现要求我们买的食物中,至少要有2000的能量,55的蛋白质,800的钙,怎样买最省钱?
设买燕麦、全奶、樱桃派、猪肉为x1,x2,x3,x4,于是可以写出如下的不等式组:

min3x1+9x2+20x3+19x4moneys.t.110x1+160x2+420x3+260x42000energy4x1+8x2+4x3+14x455protein2x1+285x2+22x3+80x4800calciumx1,x2,x3,x40

"""
对偶单纯形法
min z=15*x1+24*x2+5*x3
      6*x2+x3>=2
      5*x1+2*x2+x3>=1
      x1,x2>=0
标准化后格式
max z=-15*x1-24*x2-5*x3
      -6*x2-x3+x4=-2
      -5*x1-2*x2-x3+x5=-1
      x1,x2,x3,x4,x5>=0     
"""
import numpy as np

# 根据线性规划要改变的数据
Cj = [-3, -9, -20, -19, 0, 0, 0]  # Coefficients of the objective function
constraints_matrix = [
    [-2000, -110, -160, -420, -260, 1, 0, 0],
    [-55, -4, -8, -4, -14, 0, 1, 0],
    [-800, -2, -285, -22, -80, 0, 0, 1]
]  # Constraints matrix (with RHS)
z = [0, -3, -9, -20, -19, 0, 0, 0]  # Z-row
Xb = [5, 6, 7]  # Basis variables (indices of slack variables)
Cb = [0, 0, 0]  # Coefficients of basis variables

# 为便于表示而生成
C = [0] + Cj

# Output simplex table
def show():
    print("--" * 50)
    print("|               Cj               ", end='|')
    for i in Cj:
        print(f"{i:.2f}".center(10), end='|')
    print()
    print("--" * 50)
    print("|    Cb    |    Xb    |    b     ", end='|')
    for i in range(len(Cj)):
        print(("X" + str(i + 1)).center(10), end='|')
    print()
    print("--" * 50)
    for i in range(len(constraints_matrix)):
        print("|", end="")
        print(f"{Cb[i]:.2f}".center(10), end='|')
        print(("X" + str(Xb[i])).center(10), end='|')
        for j in range(len(constraints_matrix[0])):
            print(f"{constraints_matrix[i][j]:.2f}".center(10), end='|')
        print()
    print("--" * 50)
    print("|          Z          ", end='|')
    for i in range(len(z)):
        print(f"{z[i]:.2f}".center(10), end='|')
    print()
    print("--" * 50)
    print("**" * 50)

def fu():
    global constraints_matrix, z, Xb, Cb  # Declare global variables

    # Extracting the first column
    first_column = [row[0] for row in constraints_matrix]

    # Finding the index of the minimum value in the first column
    min_index = first_column.index(min(first_column))

    # Extract the row corresponding to min_index
    min_row = constraints_matrix[min_index]

    # Calculate the ratios z / min_row
    ratios = [z[i] / min_row[i] if min_row[i] != 0 else float('inf') for i in range(len(z))]

    # Filter out the non-positive values from z and corresponding min_row
    filtered_ratios = [ratios[i] for i in range(len(ratios)) if z[i] < 0 and min_row[i] < 0]

    # Find the minimum ratio and its index in z
    if filtered_ratios:
        min_ratio = min(filtered_ratios)
        min_ratio_index = ratios.index(min_ratio)
    else:
        print("No valid ratio found")
        return

    # Updating Xb
    Xb[min_index] = min_ratio_index 
    print("Updated basis variables Xb:")
    print(Xb)

    # Updating Cb based on Xb
    Cb = [C[Xb[i]] for i in range(len(Xb))]
    print("Updated coefficients of basis variables Cb:")
    print(Cb)

    # 根据 Xb 提取列以形成矩阵 B
    B = [[row[xb] for xb in Xb] for row in constraints_matrix]

    try:
        # Calculate the inverse of matrix B
        B_matrix = np.array(B)
        B_inv = np.linalg.inv(B_matrix)
    except np.linalg.LinAlgError:
        print("Matrix B is singular, skipping this iteration.")
        return

    # Update z
    z_update = (np.array(C[1:]) - np.dot(np.array(Cb), np.dot(B_inv, np.array(constraints_matrix)[:, 1:]))).tolist()
    z = [z[0]] + z_update  # Add the first element back to z

    # Update constraints_matrix with the inverse of B
    constraints_matrix = np.dot(B_inv, np.array(constraints_matrix)).tolist()

# 输出最优解和最优值
def print_optimal_solution():
    # 最优解:基变量的值
    optimal_solution = [0] * len(Cj)  # 初始所有变量的值为0
    for i, xb in enumerate(Xb):
        optimal_solution[xb - 1] = constraints_matrix[i][0]  # 设置基变量的值

    # 最优值:计算目标函数值
    optimal_value = sum(Cj[i] * optimal_solution[i] for i in range(len(Cj)))
    optimal_value = -optimal_value  # 取相反数

    # 打印最优解和最优值,保留两位小数
    print(f"最优解(变量的取值):{[round(val, 2) for val in optimal_solution]}")
    print(f"最优值(目标函数值的相反数):{optimal_value:.2f}")

# 迭代执行对偶单纯形法
while any(row[0] < 0 for row in constraints_matrix):
    fu()
    show()

# 输出最后的最优解和最优值
print_optimal_solution()
#最终的单纯形表
Updated basis variables Xb:
[1, 6, 2]
Updated coefficients of basis variables Cb:
[-3, 0, -9]
----------------------------------------------------------------------------------------------------
|               Cj               |  -3.00   |  -9.00   |  -20.00  |  -19.00  |   0.00   |   0.00   |   0.00   |
----------------------------------------------------------------------------------------------------
|    Cb    |    Xb    |    b     |    X1    |    X2    |    X3    |    X4    |    X5    |    X6    |    X7    |
----------------------------------------------------------------------------------------------------
|  -3.00   |    X1    |  14.24   |   1.00   |   0.00   |   3.74   |   1.98   |  -0.01   |   0.00   |   0.01   |
|   0.00   |    X6    |  23.63   |   0.00   |   0.00   |  11.38   |  -3.96   |  -0.04   |   1.00   |  -0.01   |
|  -9.00   |    X2    |   2.71   |   0.00   |   1.00   |   0.05   |   0.27   |   0.00   |   0.00   |  -0.00   |
----------------------------------------------------------------------------------------------------
|          Z          |   0.00   |   0.00   |   0.00   |  -8.31   |  -10.67  |  -0.03   |   0.00   |  -0.02   |
----------------------------------------------------------------------------------------------------

最优解(变量的取值):[14.24, 2.71, 0, 0, 0, 23.63, 0]
最优值(目标函数值的相反数):67.10

总结

单纯形法和对偶单纯形法是线性规划中最常用的两种算法,它们在解决优化问题方面发挥着关键作用。两种方法的有效结合和运用,可以大幅提升线性规划问题的求解效率和灵活性。理解和掌握这两种方法对于优化问题的解决至关重要,它不仅帮助优化计算速度和准确性,还能在面对不同类型的线性规划问题时,选择最适合的算法。无论是在理论研究还是实际应用中,这两种方法都提供了强大的工具,帮助我们应对各种复杂的决策问题。

参考文献

  1. 线性规划-单纯形算法详解
  2. 对偶单纯形法
posted @   郝hai  阅读(641)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示