Python Mathematical Modeling - Linear Programming

        I am Yuantongxue. This article is based on the content of the book "Mathematical Modeling Algorithms and Programs" written by Mr. Si Shoukui, using the examples in the book. The programming languages ​​in the book are MATLAB and Lingo. I will use Python to solve the problem. For the next month I will learn to use Python to solve mathematical modeling problems. Notes and some cases during the study will be recorded.


 

1. Linear programming

1.1 Examples and definitions of linear programming

Example 1 A machine tool factory produces two kinds of machine tools A and B, and the profit after each sale is 4,000 yuan and 3,000 yuan respectively. The production of machine tool A needs to be processed by machines A and B, and the processing time is 2 hours and 1 hour for each machine. If the number of machine hours available for processing per day is 10 hours for machine A, 8 hours for machine B and 7 hours for machine C, how many machine tools A and B should be produced by the factory to maximize the total profit?

Mathematical model of the above problem: Assuming that the factory produces x_{1}the x_{2}largest total profit when producing machines A and B, x_{1}, x_{2}it should satisfy:

The variables here are x_{1}, x_{2}called decision variables, (1) is called the objective function of the problem, and several inequalities in (2) are the constraints of the problem, denoted as st (ie subject to). Since the above objective function and constraints are both linear functions, it is called a linear programming problem. In short, a linear programming problem is a problem of finding the maximum or minimum of a linear objective function under the constraints of a set of linear constraints.


1.2 Use the scipy library in Python to solve the column 1 linear programming problem:

The standard form of linear programming is

\begin{gathered} \min c^{T} x \\ \text { s.t. }\left\{\begin{array}{l} A x \leq b \\ A e q \cdot x=b e q \\ l b \leq x \leq u b \end{array}\right. \end{gathered}                                                  (3)

Among them, c is the value vector; A and b correspond to linear inequality constraints; Aeq and beq correspond to linear equality constraints; bounds corresponds to lb and ub in the formula, the lower and upper bounds of the decision vector.

scipy.optimize.linprog(cA_ub=Noneb_ub=NoneA_eq=Noneb_eq=Nonebounds=Nonemethod='simplex'callback=Noneoptions=None)

Input parameter parsing:

  • c: coefficients of the linear objective function
  • A_ub (optional parameter): Inequality constraint matrix, A_{ub}each row of which specifies the coefficients of the linear inequality constraints on x
  • b_ub (optional parameter): vector of inequality constraints, each element represents A_{ub}the upper bound of x
  • A_eq (optional parameter): equality constraints matrix, A_{eq}each row of which specifies the coefficients of the linear equality constraints on x xx
  • b_eq (optional parameter): vector of equality constraints, A_{eq}each element of which must be equal to b_{eq }the corresponding element
  • bounds (optional parameter): defines the minimum and maximum value of the decision variable x
    • Data type: (min, max) sequence pair
    • None: Use None for no bounds, by default the bounds are (0, None) (all decision variables are non-negative)
    • If a tuple (min, max) is provided, the min and max values ​​will be used as bounds for all decision variables.

Output parameter parsing:

  • x: the value of the decision variable that minimizes the objective function while satisfying the constraints
  • fun: the optimal value of the objective function ( c^{^{T}}x)
  • slack: slack value for inequality constraints (nominally positive)b_{ub}-A_{ub}x
  • con: residuals for equality constraints (nominally zero)b_{eq}-A_{eq}x
  • success: True when the algorithm successfully found the best solution
  • status: an integer representing the exit status of the algorithm
    • 0 : Optimization terminated successfully
    • 1 : iteration limit reached
    • 2 : The problem doesn't seem feasible
    • 3 : The problem seems to be non-convergence
    • 4 : Encountered numerical difficulties
  • nit: total number of iterations performed in all stages
  • message: String descriptor of the exit status of the algorithm

Use the scipy library to solve example 1:

from scipy.optimize import linprog
c = [-4, -3]   #目标函数的系数
A= [[2, 1], [1, 1],[0,1]]  #<=不等式左侧未知数系数矩阵
b = [10,8,7]    # <=不等式右侧常数矩阵
#A_eq = [[]]   #等式左侧未知数系数矩阵
#B_eq = []     #等式右侧常数矩阵
x1 = [0, None]  #未知数取值范围
x2 = [0, None]  #未知数取值范围
res =linprog(c, A, b, bounds=(x1, x2))#默认求解最小值,求解最大值使用-c并取结果相反数
print(res)

Output result:

     con: array([], dtype=float64)
     fun: -25.999999999841208
 message: 'Optimization terminated successfully.'
     nit: 5
   slack: array([8.02629074e-11, 3.92663679e-11, 1.00000000e+00])
  status: 0
 success: True
       x: array([2., 6.])

fun is the optimal value of the objective function, slack is the slack variable, status is the optimization result state, and x is the optimal solution.

The solution result of this model is: when x1=2, x2=6, the function obtains the optimal value of 25.999999999841208.


Example 2 Solve the following linear programming problem:

\begin{gathered} \max z=2x_{1}+3x_{2}-5x_{3} \\ \text { s.t. }\left\{\begin{array}{l}x _{1}+x _{2}+x _{3}=7 \\2x _{1}-5x _{2}+x _{3}\geq 10 \\x _{1}+3x _{2}+x _{3}\leq 12 \\ x _{1},x _{2},x _{3}\geq 0 \end{array}\right. \end{gathered}

Be careful to standardize first!

Here is another more canonical way of writing:

from scipy import optimize
import numpy as np
c = np.array([2,3,-5])
A = np.array([[-2,5,-1],[1,3,1]])
B = np.array([-10,12])
Aeq = np.array([[1,1,1]])
Beq = np.array([7])
x1 = [0, None]  #未知数取值范围
x2 = [0, None]  #未知数取值范围
x3 = [0, None]  #未知数取值范围
res = optimize.linprog(-c,A,B,Aeq,Beq,bounds=(x1, x2, x3))
print(res)

Output result: 

     con: array([1.80713489e-09])
     fun: -14.571428565645054
 message: 'Optimization terminated successfully.'
     nit: 5
   slack: array([-2.24579466e-10,  3.85714286e+00])
  status: 0
 success: True
       x: array([6.42857143e+00, 5.71428571e-01, 2.35900788e-10])

The solution result of this model is: when x1=6.4, x2=5.7, x3=2.3, the function obtains the optimal value of 14.571428565645054

Example 3 Solving a linear programming problem:

\begin{gathered} \min z=2x_{1}+3x_{2} + x_{3} \\ \text { s.t. }\left\{\begin{array}{l}x _{1}+4x _{2}+2x _{3} \geq 8 \\ 3x _{1} + 2x _{2} \geq 6 \\ x _{1},x _{2},x _{3}\geq 0 \end{array}\right. \end{gathered}

Written in Python:

from scipy import optimize
import numpy as np
c = np.array([2,3,1])
A = np.array([[1,4,2],[3,2,0]])
B = np.array([8,6])
x1 = [0, None]  #未知数取值范围
x2 = [0, None]  #未知数取值范围
x3 = [0, None]  #未知数取值范围
res = optimize.linprog(c,-A,-B,bounds=(x1, x2, x3))
print(res)

Output result:

     con: array([], dtype=float64)
     fun: 6.999999994872993
 message: 'Optimization terminated successfully.'
     nit: 3
   slack: array([ 3.85261245e-09, -1.41066261e-08])
  status: 0
 success: True
       x: array([1.17949641, 1.23075538, 0.94874104])

The solution result of this model is: when x1=1.1, x2=1.2, x3=0.9, the function obtains the optimal value of 6.999999994872993

2. Transportation problems

2.1 Mathematical modeling process of production and sales balance:

        Example 6 A commodity has ma place of origin and na place of sale, the output of each place of production is a_{1},... ,a_{m}, and the demand of each place of sale is b_{1},... ,b_{n} . If the   unit freight rate of the product from the place of i origin to the  place of sale is , how should the transportation be adjusted to make the total freight cost the most economical?jc_{ij}

        Solution: Introduce a variable x_{ij}whose value  is the quantity of the commodity i shipped from the place of origin to  j the place of sale. The mathematical model is

\begin{array}{l} \min \sum_{i=1}^{m} \sum_{j=1}^{n} c_{i j} x_{i j} \\ \text { s.t. }\left\{\begin{array}{l} \sum_{j=1}^{n} x_{i j}=a_{i}, \quad i=1, \cdots, m \\ \sum_{i=1}^{m} x_{i j}=b_{j}, \quad j=1,2, \cdots, n \\ x_{i j} \geq 0 \end{array}\right. \end{array}

Obviously a linear programming problem, which can of course be solved by the simplex method.

For the transportation problem of production and sales balance, there are the following relations:

\sum_{j=1}^{n} b_{j}=\sum_{i=1}^{m}\left(\sum_{j=1}^{n} x_{i j}\right)=\sum_{j=1}^{n}\left(\sum_{i=1}^{m} x_{i j}\right)=\sum_{i=1}^{m} a_{i}

The coefficient matrix of its constraints is quite special, and a relatively simple calculation method can be used, which is customarily called the homework method on the table (proposed by Kantorovich and Hitchcock independently, referred to as the Kang-Hitch table work method) , the following uses an example to explain.

2.2 The transportation problem of production and sales balance

1. Problem description

A company has three factories, A, B, and C, which provide products to A, B, C, and D, respectively. The output, demand, and the freight rate (unit: yuan/ton) from the factory to the sales place are as follows As shown, find the best transportation plan that minimizes the cost.

factory \ sales A B C D Production (tons)
First 8 6 10 9 18
Second 9 12 13 1 18
C 14 12 16 5 19
Sales (tons) 16 15 7 17 55

2. Problem Analysis

Total output=18+18+19=55

Total sales=16+15+7+17=55

The output is equal to the sales, that is, it is a transportation problem of the balance between production and sales. Solve directly using the work method on the table.

3. Use python to solve:

# 运输问题求解:使用Vogel逼近法寻找初始基本可行解
import numpy as np
import copy
import pandas as pd


def main():
    # mat = pd.read_csv('表上作业法求解运输问题.csv', header=None).values
    # mat = pd.read_excel('表上作业法求解运输问题.xlsx', header=None).values
    a = np.array([18, 18, 19])  # 产量
    b = np.array([16, 15, 7, 17])  # 销量
    c = np.array([[8, 6, 10, 9], [9, 12, 13, 7], [14, 12, 16, 5]])
    # [c, x] = TP_vogel(mat)
    [c,x]=TP_vogel([c,a,b])


def TP_split_matrix(mat):  # 运输分割矩阵
    c = mat[:-1, :-1]
    a = mat[:-1, -1]
    b = mat[-1, :-1]
    return (c, a, b)


def TP_vogel(var):  # Vogel法代码,变量var可以是以numpy.ndarray保存的运输表,或以tuple或list保存的(成本矩阵,供给向量,需求向量)
    import numpy
    typevar1 = type(var) == numpy.ndarray
    typevar2 = type(var) == tuple
    typevar3 = type(var) == list
    if typevar1 == False and typevar2 == False and typevar3 == False:
        print('>>>非法变量<<<')
        (cost, x) = (None, None)
    else:
        if typevar1 == True:
            [c, a, b] = TP_split_matrix(var)
        elif typevar2 == True or typevar3 == True:
            [c, a, b] = var
        cost = copy.deepcopy(c)
        x = np.zeros(c.shape)
        M = pow(10, 9)
        for factor in c.reshape(1, -1)[0]:
            p = 1
            while int(factor) != M:
                if np.all(c == M):
                    break
                else:
                    print('第1次迭代!')
                    print('c:\n', c)
                    # 获取行/列最小值数组
                    row_mini1 = []
                    row_mini2 = []
                    for row in range(c.shape[0]):
                        Row = list(c[row, :])
                        row_min = min(Row)
                        row_mini1.append(row_min)
                        Row.remove(row_min)
                        row_2nd_min = min(Row)
                        row_mini2.append(row_2nd_min)
                    # print(row_mini1,'\n',row_mini2)
                    r_pun = [row_mini2[i] - row_mini1[i] for i in range(len(row_mini1))]
                    print('行罚数:', r_pun)
                    # 计算列罚数
                    col_mini1 = []
                    col_mini2 = []
                    for col in range(c.shape[1]):
                        Col = list(c[:, col])
                        col_min = min(Col)
                        col_mini1.append(col_min)
                        Col.remove(col_min)
                        col_2nd_min = min(Col)
                        col_mini2.append(col_2nd_min)
                    c_pun = [col_mini2[i] - col_mini1[i] for i in range(len(col_mini1))]
                    print('列罚数:', c_pun)
                    pun = copy.deepcopy(r_pun)
                    pun.extend(c_pun)
                    print('罚数向量:', pun)
                    max_pun = max(pun)
                    max_pun_index = pun.index(max(pun))
                    max_pun_num = max_pun_index + 1
                    print('最大罚数:', max_pun, '元素序号:', max_pun_num)
                    if max_pun_num <= len(r_pun):
                        row_num = max_pun_num
                        print('对第', row_num, '行进行操作:')
                        row_index = row_num - 1
                        catch_row = c[row_index, :]
                        print(catch_row)
                        min_cost_colindex = int(np.argwhere(catch_row == min(catch_row)))
                        print('最小成本所在列索引:', min_cost_colindex)
                        if a[row_index] <= b[min_cost_colindex]:
                            x[row_index, min_cost_colindex] = a[row_index]
                            c1 = copy.deepcopy(c)
                            c1[row_index, :] = [M] * c1.shape[1]
                            b[min_cost_colindex] -= a[row_index]
                            a[row_index] -= a[row_index]
                        else:
                            x[row_index, min_cost_colindex] = b[min_cost_colindex]
                            c1 = copy.deepcopy(c)
                            c1[:, min_cost_colindex] = [M] * c1.shape[0]
                            a[row_index] -= b[min_cost_colindex]
                            b[min_cost_colindex] -= b[min_cost_colindex]
                    else:
                        col_num = max_pun_num - len(r_pun)
                        col_index = col_num - 1
                        print('对第', col_num, '列进行操作:')
                        catch_col = c[:, col_index]
                        print(catch_col)
                        # 寻找最大罚数所在行/列的最小成本系数
                        min_cost_rowindex = int(np.argwhere(catch_col == min(catch_col)))
                        print('最小成本所在行索引:', min_cost_rowindex)
                        # 计算将该位置应填入x矩阵的数值(a,b中较小值)
                        if a[min_cost_rowindex] <= b[col_index]:
                            x[min_cost_rowindex, col_index] = a[min_cost_rowindex]
                            c1 = copy.deepcopy(c)
                            c1[min_cost_rowindex, :] = [M] * c1.shape[1]
                            b[col_index] -= a[min_cost_rowindex]
                            a[min_cost_rowindex] -= a[min_cost_rowindex]
                        else:
                            x[min_cost_rowindex, col_index] = b[col_index]
                            # 填入后删除已满足/耗尽资源系数的行/列,得到剩余的成本矩阵,并改写资源系数
                            c1 = copy.deepcopy(c)
                            c1[:, col_index] = [M] * c1.shape[0]
                            a[min_cost_rowindex] -= b[col_index]
                            b[col_index] -= b[col_index]
                    c = c1
                    print('本次迭代后的x矩阵:\n', x)
                    print('a:', a)
                    print('b:', b)
                    print('c:\n', c)
                if np.all(c == M):
                    print('【迭代完成】')
                    print('-' * 60)

                else:
                    print('【迭代未完成】')
                    print('-' * 60)

                    p += 1
                    print(f"第{p}次迭代!")
        total_cost = np.sum(np.multiply(x, cost))
        if np.all(a == 0):
            if np.all(b == 0):
                print('>>>供求平衡<<<')
            else:
                print('>>>供不应求,需求方有余量<<<')
        elif np.all(b == 0):
            print('>>>供大于求,供给方有余量<<<')
        else:
            print('>>>无法找到初始基可行解<<<')
        print('>>>初始基本可行解x*:\n', x)
        print('>>>当前总成本:', total_cost)
        [m, n] = x.shape
        varnum = np.array(np.nonzero(x)).shape[1]
        if varnum != m + n - 1:
            print('【注意:问题含有退化解】')
    return (cost, x)


if __name__ == '__main__':
    main()

 Output result:

D:\Anaconda\python.exe D:/PyCharm/数学建模/线性规划/www.py
第1次迭代!
c:
 [[ 8  6 10  9]
 [ 9 12 13  7]
 [14 12 16  5]]
行罚数: [2, 2, 7]
列罚数: [1, 6, 3, 2]
罚数向量: [2, 2, 7, 1, 6, 3, 2]
最大罚数: 7 元素序号: 3
对第 3 行进行操作:
[14 12 16  5]
最小成本所在列索引: 3
本次迭代后的x矩阵:
 [[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0. 17.]]
a: [18 18  2]
b: [16 15  7  0]
c:
 [[         8          6         10 1000000000]
 [         9         12         13 1000000000]
 [        14         12         16 1000000000]]
【迭代未完成】
------------------------------------------------------------
第2次迭代!
第1次迭代!
c:
 [[         8          6         10 1000000000]
 [         9         12         13 1000000000]
 [        14         12         16 1000000000]]
行罚数: [2, 3, 2]
列罚数: [1, 6, 3, 0]
罚数向量: [2, 3, 2, 1, 6, 3, 0]
最大罚数: 6 元素序号: 5
对第 2 列进行操作:
[ 6 12 12]
最小成本所在行索引: 0
本次迭代后的x矩阵:
 [[ 0. 15.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0. 17.]]
a: [ 3 18  2]
b: [16  0  7  0]
c:
 [[         8 1000000000         10 1000000000]
 [         9 1000000000         13 1000000000]
 [        14 1000000000         16 1000000000]]
【迭代未完成】
------------------------------------------------------------
第3次迭代!
第1次迭代!
c:
 [[         8 1000000000         10 1000000000]
 [         9 1000000000         13 1000000000]
 [        14 1000000000         16 1000000000]]
行罚数: [2, 4, 2]
列罚数: [1, 0, 3, 0]
罚数向量: [2, 4, 2, 1, 0, 3, 0]
最大罚数: 4 元素序号: 2
对第 2 行进行操作:
[         9 1000000000         13 1000000000]
最小成本所在列索引: 0
本次迭代后的x矩阵:
 [[ 0. 15.  0.  0.]
 [16.  0.  0.  0.]
 [ 0.  0.  0. 17.]]
a: [3 2 2]
b: [0 0 7 0]
c:
 [[1000000000 1000000000         10 1000000000]
 [1000000000 1000000000         13 1000000000]
 [1000000000 1000000000         16 1000000000]]
【迭代未完成】
------------------------------------------------------------
第4次迭代!
第1次迭代!
c:
 [[1000000000 1000000000         10 1000000000]
 [1000000000 1000000000         13 1000000000]
 [1000000000 1000000000         16 1000000000]]
行罚数: [999999990, 999999987, 999999984]
列罚数: [0, 0, 3, 0]
罚数向量: [999999990, 999999987, 999999984, 0, 0, 3, 0]
最大罚数: 999999990 元素序号: 1
对第 1 行进行操作:
[1000000000 1000000000         10 1000000000]
最小成本所在列索引: 2
本次迭代后的x矩阵:
 [[ 0. 15.  3.  0.]
 [16.  0.  0.  0.]
 [ 0.  0.  0. 17.]]
a: [0 2 2]
b: [0 0 4 0]
c:
 [[1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000         13 1000000000]
 [1000000000 1000000000         16 1000000000]]
【迭代未完成】
------------------------------------------------------------
第5次迭代!
第1次迭代!
c:
 [[1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000         13 1000000000]
 [1000000000 1000000000         16 1000000000]]
行罚数: [0, 999999987, 999999984]
列罚数: [0, 0, 3, 0]
罚数向量: [0, 999999987, 999999984, 0, 0, 3, 0]
最大罚数: 999999987 元素序号: 2
对第 2 行进行操作:
[1000000000 1000000000         13 1000000000]
最小成本所在列索引: 2
本次迭代后的x矩阵:
 [[ 0. 15.  3.  0.]
 [16.  0.  2.  0.]
 [ 0.  0.  0. 17.]]
a: [0 0 2]
b: [0 0 2 0]
c:
 [[1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000         16 1000000000]]
【迭代未完成】
------------------------------------------------------------
第6次迭代!
第1次迭代!
c:
 [[1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000         16 1000000000]]
行罚数: [0, 0, 999999984]
列罚数: [0, 0, 999999984, 0]
罚数向量: [0, 0, 999999984, 0, 0, 999999984, 0]
最大罚数: 999999984 元素序号: 3
对第 3 行进行操作:
[1000000000 1000000000         16 1000000000]
最小成本所在列索引: 2
本次迭代后的x矩阵:
 [[ 0. 15.  3.  0.]
 [16.  0.  2.  0.]
 [ 0.  0.  2. 17.]]
a: [0 0 0]
b: [0 0 0 0]
c:
 [[1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000 1000000000 1000000000]
 [1000000000 1000000000 1000000000 1000000000]]
【迭代完成】
------------------------------------------------------------
>>>供求平衡<<<
>>>初始基本可行解x*:
 [[ 0. 15.  3.  0.]
 [16.  0.  2.  0.]
 [ 0.  0.  2. 17.]]
>>>当前总成本: 407.0

进程已结束,退出代码为 0

The method used above is the work method on the table, which is shown below:

 Final optimal total cost: 407.0

2.3 The problem of transportation where production exceeds sales

1. Problem description

There are three animal husbandry bases that provide fresh milk to 4 cities. The daily demand for fresh milk in 4 cities, the daily supply of fresh milk in 3 bases, and the cost of delivering fresh milk per kiloliter are shown in the table below. Try to determine The most economical fresh milk transportation solution.

city\base A B c D Supply
First 3 2 4 5 30
Second 2 3 5 3 40
C 1 4 2 4 50
demand 16 30 twenty four 30

2. Problem Analysis

Total supply=30+40+50=120

Total demand=16+30+24+30=100

Oversupply, that is, production exceeds sales, which is a transportation problem where production exceeds sales. In order to transform the problem into a transportation problem of balanced production and sales, the following adjustments need to be made.

(1) Add a virtual sales place E, so that the total demand is 120-100=20.

(2) Since the place of sale is virtual, in fact, the materials with excess production are stored on the spot at the place of origin, so there will be no actual transportation, that is, no freight will be incurred.

Therefore, the new supply and demand and unit tariff table are as follows.

city\base A B c D AND Supply
First 3 2 4 5 0 30
Second 2 3 5 3 0 40
C 1 4 2 4 0 50
demand 16 30 twenty four 30 20 120

3. Problem solving

Using python to achieve: 

This can also be solved using the (least element method):

'----------------------------------------最小元素法------------------------------------' \
'最小元素法是指有限满足单位运价最小的供销服务'
import numpy as np
import copy

supplyNum = 3  # 供应商数量
demandNum = 5  # 需求商数量
A = np.array([30,40,50])  # 产量
B = np.array([16,30,24,30,20])  # 销量
C = np.array([[3,2,4,5,0], [2,3,5,3,0], [1,4,2,4,0]])  # 成本
X = np.zeros((supplyNum, demandNum))  # 初始解
c = copy.deepcopy(C)
maxNumber = 10000  # 极大数


def pivot(A, B, c, X):
    index = np.where(c == np.min(c))  # 寻找最小值的索引
    minIndex = (index[0][0], index[1][0])  # 确定最小值的索引
    # 确定应该优先分配的量,并且改变价格
    if A[index[0][0]] > B[index[1][0]]:
        X[minIndex] = B[index[1][0]]
        c[:, index[1][0]] = maxNumber  # 改变价格
        A[index[0][0]] = A[index[0][0]] - B[index[1][0]]
        B[index[1][0]] = 0  # 改变销量
    else:
        X[minIndex] = A[index[0][0]]
        c[index[0][0], :] = maxNumber
        B[index[1][0]] = B[index[1][0]] - A[index[0][0]]
        A[index[0][0]] = 0
    return A, B, c, X


# 最小元素法
def minimumElementMethod(A, B, c, X):
    while (c < maxNumber).any():
        A, B, c, X = pivot(A, B, c, X)
    return X


solutionMin = minimumElementMethod(A, B, c, X)
print('最小元素法得到的初始解为:')
print(solutionMin)
print('最小元素法得到的总运费为:', ((solutionMin * C).sum()))

Output result:

最小元素法得到的初始解为:
[[ 0. 10.  0.  0. 20.]
 [ 0. 20.  0. 20.  0.]
 [16.  0. 24. 10.  0.]]
最小元素法得到的总运费为: 244.0

2.4 The transportation problem that the production is less than the sales

1. Problem description

A certain three coal plants supply 4 regions, and it is assumed that the same amount of coal has the same effect in these regions. The annual output of each coal plant, the demand in each region, and the unit freight rate from each coal plant to each region are as follows: Show, try to determine the optimal deployment plan.

Sales place\Origin B1 B2 B3 B4 Yield
A1 5 3 10 4 90
A2 2 6 9 6 40
A3 14 10 5 7 70
sales 30 50 100 40

2. Problem Analysis

Total supply=30+40+50=120

Total demand=16+30+24+30=100

Oversupply, that is, production exceeds sales, which is a transportation problem where production exceeds sales. In order to transform the problem into a transportation problem of balanced production and sales, the following adjustments need to be made.

(1) Add a virtual sales place E, so that the total demand is 120-100=20.

(2) Since the place of sale is virtual, in fact, the materials with excess production are stored on the spot at the place of origin, so there will be no actual transportation, that is, no freight will be incurred.

Therefore, the new supply and demand and unit tariff table are as follows.

Sales place\Origin B B B3 B4 Yield
A, 5 3 10 4 90
A2 2 6 9 6 40
A3 14 10 5 7 70
A4 0 0 0 0 20
sales 30 50 100 40 220

3. Problem solving

Solve using python:

Here you can use (Vogel approximation method) to find the initial basic feasible solution

'-----------------------------------Vogel法---------------------------------'
supplyNum = 4  # 供应商数量
demandNum = 4  # 需求商数量
A = np.array([90,40,70,20])  # 产量
B = np.array([30,50,100,40])  # 销量
C = np.array([[5,3,10,4], [2,6,9,6], [14,10,5,7],[0,0,0,0]])  # 成本
X = np.zeros((supplyNum, demandNum))  # 初始解
c = copy.deepcopy(C)
numPunish = [0] * (supplyNum + demandNum)  # 整体罚数


def pivot(X, A, B, C):
    # 求解罚数,其中列罚数排在行罚数后面
    for i in range(supplyNum):
        sortList = np.sort(C[i, :])
        numPunish[i] = sortList[1] - sortList[0]
    for i in range(demandNum):
        sortList = np.sort(C[:, i])
        numPunish[i + supplyNum] = sortList[1] - sortList[0]

    maxIndex = np.argmax(numPunish)  # 寻找最大罚数的索引

    # 若最大的罚数属于行罚数
    if maxIndex < supplyNum:
        minIndex = np.argmin(C[maxIndex, :])
        index = (maxIndex, minIndex)  # 得到最大罚数的一行中最小的运价
        # 若产量大于销量
        if A[maxIndex] > B[minIndex]:
            X[index] = B[minIndex]  # 更新解
            C[:, minIndex] = maxNumber  # 将已经满足的一列中的运价增大替代删除操作
            A[maxIndex] -= B[minIndex]  # 更新剩余产量
            B[minIndex] = 0  # 更新已经满足的销量
        # 若销量大于产量
        else:
            X[index] = A[maxIndex]
            C[maxIndex, :] = maxNumber
            B[minIndex] -= A[maxIndex]  # 更新销量
            A[maxIndex] = 0

    # 若最大的罚数为列罚数
    else:
        minIndex = np.argmin(C[:, maxIndex - supplyNum])  # 这时maxIndex-supplyNum是罚数最大的列
        index = (minIndex, maxIndex - supplyNum)
        if A[minIndex] > B[maxIndex - supplyNum]:  # 这里是产量大于销量,因此有限满足销量
            X[index] = B[maxIndex - supplyNum]
            C[:, maxIndex - supplyNum] = maxNumber
            A[minIndex] -= B[maxIndex - supplyNum]
            B[maxIndex - supplyNum] = 0
        else:
            X[index] = A[minIndex]
            C[minIndex, :] = maxNumber
            B[maxIndex - supplyNum] -= A[minIndex]
            A[minIndex] = 0
    return X, A, B, C


# 沃格尔法得到初始解
def Vogel(A, B, C):
    X = np.zeros((supplyNum, demandNum))  # 初始解
    numPunish = [0] * (supplyNum + demandNum)  # 整体罚数
    # 迭代,直到所有的产量和销量全部满足
    while (A != 0).any() and (B != 0).any():
        X, A, B, C = pivot(X, A, B, C)
    return X


# 上面对于条件的判断,还需要对销量和需求量进行改变
solutionVogel = Vogel(A, B, c)
print('Vogel法得到的初始解为:')
print(solutionVogel)
print('Vogel法得到的总运费为:', (C * solutionVogel).sum())

 Output result:

Vogel法得到的初始解为:
[[ 0. 50.  0. 40.]
 [30.  0. 10.  0.]
 [ 0.  0. 70.  0.]
 [ 0.  0. 20.  0.]]
Vogel法得到的总运费为: 810.0

The above three production and sales problems are common. In addition to production and sales problems, there are also transportation problems of elastic demand and transportation problems of intermediate transshipment.

I won't be wordy here, check the information yourself!

3. Assignment problem

3.1 Mathematical model of the assignment problem

        Example 7 It is proposed to assign  n people to do  na job, and each person will do one and only one job. If the second  iperson is assigned to do the first j job, it will take a c_{ij}unit of time. How should the work be allocated to minimize the total time spent by the workers?

        It is easy to see that to give an instance of an assignment problem, it is only necessary to give the matrix C=(c_{ij})Ccalled the coefficient matrix of the assignment problem. Introduce a variable that  takes = 1 if  the job is x_{ij}assigned  , and = 0 otherwise. The mathematical model of the above assignment problem is:ijx_{ij}x_{ij}

\begin{aligned} &\min \sum_{i=1}^{n} \sum_{j=1}^{n} c_{i j} x_{i j} \\ &\text { s.t. } \sum_{j=1}^{n} x_{i j}=1 \\ &\sum_{i=1}^{n} x_{i j}=1 \\ &x_{i j}=0 \text { or } 1 \end{aligned}

        The feasible solution of the above assignment problem can be represented by a matrix, each row and each column has one and only one element is 1, and the other elements are 0; it can be represented 1,...,nby a permutation of .

         The variables in the problem can only take 0 or 1, which is a 0-1 programming problem. The general 0-1 programming problem is extremely difficult to solve. However, the assignment problem is not difficult to solve. The coefficient matrix of the constraint equation system is very special (called the full unit modulus matrix, and its non-zero subformulas of all orders are ±1), and the component of its non-negative feasible solution can only take 0 or 1, so constraint x_{ij}= 0 or 1 can be rewritten as x_{ij}≥ 0 without changing its solution. At this point, the assignment problem is transformed into a special transport problem where m=n, a_{i}=b_{j}=1.

3.2 The Hungarian Algorithm for Solving the Assignment Problem

The algorithm relies mainly on the fact C=(c_{ij})that B=(b_{ij})an assignment problem with C or B as the coefficient matrix has the same optimal assignment if the same number is added or subtracted to each element in a row (or column) of the coefficient matrix, resulting in a new matrix .

Example 8 Solving the assignment problem, the coefficient matrix is

C=\left[\begin{array}{llll} 16&15&19&22\\17&21&19&18\\24&22&18&17\\17&19&22&16\end{ array}\right]

Homework solution:

 Subtract 15 from the element in the first row, the smallest element in this row, similarly, subtract 17 from the element in the second row, subtract 17 from the element in the third row, and subtract 16 from the element in the last row, we get:

B_{1}=\left[\begin{array}{llll} 1&0&4&7\\0&4&2&1\\7&5&1&0\\1&3&6&0 \end{array}\right]

Then subtract 1 from each of the elements in the third column to get

B_{2}=\left[\begin{array}{cccc} 1 & 0^{*} & 3 & 7 \\ 0^{*} & 4 & 1 & 1 \\ 7 & 5 & 0^{*} & 0 \\ 1 & 3 & 5 & 0^{*} \end{array}\right]

The assignment problem with B_{2}the coefficient matrix has an optimal assignment

\left(\begin{array}{llll}1&2&3&4\\2&1&3&4\end{array}\right)

By equivalence, it is also an optimal assignment of C.

python solution:

from scipy.optimize import linear_sum_assignment
import numpy as np
cost = np.array([[16,15,19,22], [17,21,19,18], [24,22,18,17],[17,19,22,16]])
row_ind, col_ind = linear_sum_assignment(cost)
print('row_ind:', row_ind)  # 开销矩阵对应的行索引
print('col_ind:', col_ind)  # 对应行索引的最优指派的列索引
print('cost:', cost[row_ind, col_ind])  # 提取每个行索引的最优指派列索引所在的元素,形成数组
print('cost_sum:', cost[row_ind, col_ind].sum())  # 最小开销

 Output result:

row_ind: [0 1 2 3]
col_ind: [1 0 2 3]
cost: [15 17 18 16]
cost_sum: 66

Example 9 Solving the assignment problem of the coefficient matrix C

C=\left[\begin{array}{lllll} 12 & 7 & 9 & 7 & 9 \\ 8 & 9 & 6 & 6 & 6 \\ 7 & 17 & 12 & 14 & 12 \\ 15 & 14 & 6 & 6 & 10 \\ 4 & 10 & 7 & 10 & 6 \end{array}\right]

python implementation:

from scipy.optimize import linear_sum_assignment
import numpy as np
cost = np.array([[12, 7, 9 ,7, 9], 
                 [8, 9, 6, 6, 6], 
                 [7, 17, 12, 14, 12],
                 [15,14, 6 ,6, 10],
                 [4,10 ,7 ,10 ,6]])
row_ind, col_ind = linear_sum_assignment(cost)
print('row_ind:', row_ind)  # 开销矩阵对应的行索引
print('col_ind:', col_ind)  # 对应行索引的最优指派的列索引
print('cost:', cost[row_ind, col_ind])  # 提取每个行索引的最优指派列索引所在的元素,形成数组
print('cost_sum:', cost[row_ind, col_ind].sum())  # 最小开销

Output result:

row_ind: [0 1 2 3 4]
col_ind: [1 2 0 3 4]
cost: [7 6 7 6 6]
cost_sum: 32


4. Duality Theory and Sensitivity Analysis

4.1 Primitive and Dual Problems

Consider the following pair of linear programming models:

\max c^{T} x \quad \text { s.t. } \quad A x \leq b, x \geq 0                                                                                         (P)

and

\min b^{T} y \quad \text { s.t. } \quad A^{T} y \geq c, y \geq 0                                                                                        (D)

Call (P) the primal problem and (D) its dual problem.

Less rigorously, the dual problem can be viewed as a "row-column transpose" of the original problem:

(1) The coefficients of the jth column in the original problem are the same as the coefficients of the jth row in the dual problem;

(2) Each coefficient row of the original objective function is the same as each constant column on the right side of its dual problem;

(3) Each constant column on the right side of the original problem is the same as each coefficient row of its dual objective function;

(4) In this pair of problems, the inequality direction and the optimization direction are opposite.

Consider linear programming:

\max c^{T} x \quad \text { s.t. } \quad A x \leq b, x \geq 0

 Turning the equality constraints into inequality constraints, we can get

\min c^{T} x \quad \text { s.t. }\left[\begin{array}{c} A \\ -A \end{array}\right] x \geq\left[\begin{array}{c} b \\ -b \end{array}\right], x \geq 0

Its dual problem is

\max \left[\begin{array}{ll} b^{T} & -b^{T} \end{array}\right]\left[\begin{array}{l} y_{1} \\ y_{2} \end{array}\right] \quad \text { s.t. }\left[\begin{array}{ll} A^{T} & -A^{T} \end{array}\right]\left[\begin{array}{l} y_{1} \\ y_{2} \end{array}\right] \leq c

where y_{1}and represent  the dual variable groups y_{2}corresponding to the constraint  Ah\geq b sum  , respectively. -Ax\geq -b, then the y=y_{1}-y_{2}above formula can be written as

\min b^{T} y \quad \text { s.t. } \quad A^{T} y \geq c, y \geq 0

 The relationship between the primal problem and the dual constraints of the dual:

The formula:

The inequality sign is in the same direction: the inequality sign of the Max variable is in the same direction as the inequality sign of the min constraint

The direction of the inequality sign is opposite: The inequality sign of the Max constraint is opposite to the inequality sign of the min variable. The equal sign corresponds to no constraint:

The "=" of the constraint condition corresponds to the variable "unconstrained"
 

Example 1:

Try to solve the dual problem of the following linear programming.

\begin{aligned} &\min Z=2 x_{1}+3 x_{2}-5 x_{3}+x_{4} \\ &\left\{\begin{array}{l} x_{1}+x_{2}-3 x_{3}+x_{4} \geq 5 \\ 2 x_{1}+2 x_{3}-x_{4} \leq 4 \\ x_{2}+x_{3}+x_{4}=6 \\ x_{1} \leq 0 ; x_{2}, x_{3} \geq 0 ; x_{4} \epsilon R\end{array}\right. \end{aligned}

Solution: Let the dual variables corresponding to the three constraints be y1, y2, and y3 respectively, then the dual problem of the original problem is:
\begin{aligned} &\max Z^{\prime}=5 y_{1}+4 y_{2}+6 y_{3} \\ &\left\{\begin{array}{l} y_{1 }+2 y_{2} \geq 2 \\ y_{1}+y_{3} \leq 3 \\ -3 y_{1}+2 y_{2}+y_{3} \leq-5 \\ y_ {1}-y_{2}+y_{3}=1 \\ y_{1} \geq 0 ;  y 2 ≤ 0 ;  y_{3} \epsilon R \end{array}\right.  \end{aligned}

4.2 Basic properties of dual problems

The primal and dual problems:

\begin {array}{ll} \max Z=CX & \min Z^{\prime}=Y b \\ \begin{cases}AX \leq b \\ X \geq 0\end{cases} & \left \{\begin{array}{l} YA \geq C \\ Y \geq 0 \end{array}\right.  \end{array}

(1) Symmetry
The dual of the dual problem is the original problem
(2) Weak duality
If\overline{X} the feasible solution of the original problem (max) is a feasible solution \overline{Y}of the dual problem (min), then C\overline{X}\leq \overline{Y}b, prove:

max problem: A\overline{X}\leq b    left multiplication\overline{Y}     \overline{Y}A\overline{X}\leq \overline{Y}b

min problem: \overline{Y}A\geq C     right multiplication\overline{X}     \overline{Y}A\overline{X}\geq C\overline{X}

(3) Unbounded
If the original problem (dual problem) has an unbounded solution, then its dual problem (original problem) has no feasible solution
 

 (4) Optimality
Let \widehat{X}it be the feasible solution of the original problem and the feasible solution \widehat{Y}of the dual problem, when C\widehat{X}=\widehat{Y}b= , it \widehat{X},\widehat{Y}is the optimal solution

(5) Duality Theorem
If the original problem has an optimal solution, then the dual problem also has an optimal solution, and the daily scalar function values ​​are equal

(6) Complementary slackness
Let \widehat{X},\widehat{Y}be the feasible solutions of the primal problem and the dual problem, respectively, and the feasible solutions X_{s},Y_{s}of their slack variables, respectively, then \widehat{X},\widehat{Y}the optimal solution is the optimal solution if and only if Y_{s}\widehat{X}=0andX_{s}\widehat{Y}=0

 Example 10 Known linear programming problem:

The optimal solution to its dual problem is known to be y_ {1} ^ {*} = \ frac {4} {5}, y_ {2} ^ {*} = \ frac {3} {5}, z = 5. Trying out duality theory to find the optimal solution of the original problem

The solution is to write its dual problem first

\begin{aligned} &\max z=4 y_{1}+3 y_{2} \\ &\left\{\begin{array}{l} y_{1}+2 y_{2} \leq 2 \ \ y_{1}-y_{2} \leq 3 \\ 2 y_{1}+3 y_{2} \leq 5 \\ y_{1}+y_{2} \leq 2 \\ 3 y_{1} +y_{2} \leq 3 \\ y_{1}, y_{2} \geq 0 \end{array}\right.  \end{aligned}     Add slack variables     \left\{\begin{array}{l} y_{1}+2 y_{2}+y_{s_{1}}=2 \\ y_{1}-y_{2}+y_{s_{2} }=3 \\ 2 y_{1}+3 y_{2}+y_{s_{3}}=5 \\ y_{1}+y_{2}+y_{s_{4}}=2 \\ 3 y_{1}+y_{2}+y_{s_{5}}=3 \\ y_{1}, y_{2}, y_{s_{1}}, y_{s_{2}}, y_{s_ {3}}, y_{s_{4}}, y_{s_{5}} \geq 0 \end{array}\right.    into the optimal solution 


 \left\{\begin{array}{l} y_{s_{1}}=0 \\ y_{s_{2}} \neq 0 \\ y_{s_{3}} \neq 0 \\ y_{s_ {4}} \neq 0 \\y_{s_{5}}=0 \end{array}\right.    Complementary Relaxation        \left\{\begin{array}{l} y_{s_{2}} x_{2}^{*}=0 \\ y_{s_{3}} x_{3}^{*}=0 \\ y_{s_{4}} x_{4}^{*}=0 \end{array}\right.         Solving      \left\{\begin{array}{l} x_{2}^{*}=0 \\ x_{3}^{*}=0 \\ x_{4}^{*}=0 \end{array}\right.


Re-analyze the original problem:
\left\{\begin{array}{l} x_{1}+x_{2}+2 x_{3}+x_{4}+3 x_{5}-x_{s_{1}}=4 \\ 2 x_{1}-x_{2}+3 x_{3}+x_{4}+x_{5}-x_{s_{2}}=3 \\ x_{1}, x_{2}, x_{3}, x_{4}, x_{5}, x_{s_{1}}, x_{s_{2}} \geq 0 \end{array}\right.     since  y_{1}^{*} \neq 0, y_{2}^{*} \neq 0      \left\{\begin{array}{l} x_{s_{1}}=0 \\ x_{s_{2}}=0 \end{array}\right.     the 


x_{s_{1}}, x_{s_{2}}, x_{2}^{*}, x_{3}^{*}, x_{4}^{*}Substitute the \left\{\begin{array}{l} x_{1}^{*}+3 x_{5}^{*}=4 \\ 2 x_{1}^{*}+x_{5}^{*}=3 \end{array}\right.   solution to    get the \left\{\begin{array}{l} x_{1}^{*}=1 \\ x_{5}^{*}=1 \end{array}\right.  optimal solution  :  \begin{aligned} &X^{*}=(1,0,0,0,1)^{T} \\ &\omega^{*}=5 \end{aligned} 


4.3 Sensitivity analysis

        Two questions are raised: when one or more of these coefficients change, what will happen to the optimal solution of the linear programming problem that has been obtained; or when these coefficients change, what is the optimal solution of the linear programming problem? The solution or optimal basis does not change. We will not discuss it here.

4.4 Parametric Linear Programming

Parametric linear programming is to study a_{ij},b_{i},c_{j} the value of each critical point at which the optimal solution changes when one of these parameters changes continuously. That is, a parameter is used as a parameter, and the objective function is a linear function of this parameter in a certain interval, and the constraint condition containing this parameter is a linear equation or an inequality. Therefore, the simplex method and the dual simplex method can still be used to analyze the parametric linear programming problem.

4.4.1 Simplex method:

Example: Solving a Linear Programming Problem Using the Simplex Method
\begin{aligned} &\max z=2 x_{1}+x_{3} \\ &\text { s.t. }\left\{\begin{array}{c} 5 x_{2} \leq 15 \\ 6 x_{1}+2 x_{2} \leq 24 \\ x_{1}+x_{2} \leq 5 \\ x_{1}, x_{2} \geq 0 \end{array}\right. \end{aligned}

(1) Standardization

\begin{aligned} &\max z=2 x_{1}+x_{2}+0 x_{3}+0 x_{4}+0 x_{5} \\ &\text { s.t. }\left\{\begin{array}{c} 5 x_{2}+x_{3}=15 \\ 6 x_{1}+2 x_{2}+x_{4}=24 \\ x_{1}+x_{2}+x_{5}=5 \\ x_{1}, x_{2}, x_{3}, x_{4}, x_{5} \geq 0 \end{array}\right. \end{aligned}

(2) Use python programming:

import numpy as np
class Simplex:
    def __init__(self) -> None:
        self.solutionStatus = 0

    def set_param(self, A, b, c, baseInd):

        self.A = A
        self.m, self.n = self.A.shape
        self.b = b
        self.c = c
        self.baseInd = baseInd
        self.nonbaseInd = [i for i in range(self.n) if i not in self.baseInd]
        self.B = self.A[:, self.baseInd]
        self.N = self.A[:, self.nonbaseInd]
        self.B_inv = self.B.I

    def main(self):
        # 计算非基变量的检验数,基变量检验数为0
        reducedCost = [0] * self.n
        X_B = None
        simplexTable = SimplexTable()
        while (self.solutionStatus == 0):
            for i in range(self.n):
                reducedCost[i] = float(self.c[:, i] - np.dot(self.c[:, self.baseInd], self.A[:, i]))
                # p_j已经全部左乘B_inv,因此这里c_B可以直接与更新后的p_j相乘
            self.solutionStatus = self.check_solutionStatus(reducedCost)
            simplexTable.show(self.A, self.b, self.c, reducedCost, self.baseInd, self.solutionStatus)
            if self.solutionStatus == 0:
                inVarInd = reducedCost.index(max(reducedCost))
                outVarInd = self.get_outVar(inVarInd)
                self.baseInd[self.baseInd.index(outVarInd)] = inVarInd
                self.nonbaseInd[self.nonbaseInd.index(inVarInd)] = outVarInd
                # 得到新基
                self.B = self.A[:, self.baseInd]
                self.B_inv = self.B.I
                self.b = np.dot(self.B_inv, self.b)
                X_B = self.b.T.tolist()[0]
                self.A[:, inVarInd] = self.A[:, outVarInd]
                self.A[:, self.nonbaseInd] = np.dot(self.B_inv, self.A[:, self.nonbaseInd])
            else:
                break
        if self.solutionStatus == 1:
            solution = {}
            for i in range(self.n):
                if i in self.baseInd:
                    solution[i] = X_B[self.baseInd.index(i)]
                if i in self.nonbaseInd:
                    solution[i] = 0
            objctive = float(np.dot(np.dot(self.c[:, self.baseInd], self.B_inv), self.b))

            return solution, objctive, self.solutionStatus
        else:
            solution = None
            objctive = None
            return solution, objctive, self.solutionStatus

    def check_solutionStatus(self, reducedCost):
        if all(x < 0 or x == 0 for x in reducedCost):
            # 所有检验数<=0
            if any(reducedCost[i] == 0 for i in self.nonbaseInd):
                # 存在非基变量的检验数为0
                return 2  # 无穷多最优解
            else:
                return 1  # 唯一最优解
        else:
            if all(all(x < 0 for x in self.A[:, i]) for i in self.nonbaseInd if reducedCost[i] > 0):
                return 3  # 无界解
            else:
                # 选择最大检验数对应的非基变量入基
                return 0

    def get_outVar(self, inVarInd):
        inVarCoef = self.A[:, inVarInd]
        ratio = [np.inf] * self.m
        for i in range(len(inVarCoef)):
            if float(inVarCoef[i, :]) > 0:
                ratio[i] = float(self.b[i, :]) / float(inVarCoef[i, :])
        outVarInd = self.baseInd[ratio.index(min(ratio))]
        return outVarInd

class SimplexTable:
    def __init__(self):
        # 左端表头格式设置
        self.setting_left = '{:^8}'+'{:^8}'+'{:^8}|'
        # 右端变量格式设置
        self.setting_var = '{:^8}|'
        # 求解状态格式设置
        self.setting_status = '{:^24}|'+'{:^44}|'
    def show(self,A,b,c,reducedCost,baseInd,solutionStatus):
        var_num = A.shape[1]
        # 打印系数行
        c_j_list = c.flatten().tolist()[0]
        print('='*80)
        print(self.setting_left.format('','c_j',''),end = '')
        for i in c_j_list:
            print(self.setting_var.format(i),end = '')
        print()
        # 打印变量行
        print(self.setting_left.format('c_B','x_B','b'),end = '')
        for i in range(var_num):
            print(self.setting_var.format('x'+str(i)),end = '')
        print()
        # 打印变量与矩阵
        for i in range(len(baseInd)):
            varInd = baseInd[i]
            varCeof = round(float(c[:,varInd]),2)
            varValue = round(float(b[i,:]),2)
            row = A[i,:].flatten().tolist()[0]
            print(self.setting_left.format(str(varCeof),'x'+str(varInd),str(varValue)),end='')
            for i in range(var_num):
                print(self.setting_var.format(round(row[i],2)),end = '')
            print()
        # 打印检验数
        print(self.setting_left.format('','c_j-z_j',''),end='')
        for i in reducedCost:
            print(self.setting_var.format(round(i,2)),end = '')
        print()
        # 显示求解状态
        if solutionStatus == 0:
            print(self.setting_status.format('status','...Iteration continues...'))
        if solutionStatus == 1:
            print(self.setting_status.format('status','Unique Optimal Solution'))
            B = A[:,baseInd]
            B_inv =  B.I
            objctive = float(np.dot(np.dot(c[:, baseInd], B_inv), b))
            print(self.setting_status.format('objctive', round(objctive,2)))
            solution = [0]*var_num
            X_B = b.T.tolist()[0]
            for i in range(var_num):
                if i in baseInd:
                    solution[i] = X_B[baseInd.index(i)]
            print(self.setting_left.format('', 'solution', ''), end='')
            for i in solution:
                print(self.setting_var.format(round(i, 2)), end='')
            print()

        if solutionStatus == 2:
            print(self.setting_status.format('status','Multiple Optimal Solutions'))
            B = A[:, baseInd]
            B_inv = B.I
            objctive = float(np.dot(np.dot(c[:, baseInd], B_inv), b))
            print(self.setting_status.format('objctive', round(objctive, 2)))
        if solutionStatus == 3:
            print(self.setting_status.format('status','Unboundedness'))

'''=============================================================================='''
A = np.matrix([[0,5,1,0,0],
               [6,2,0,1,0],
               [1,1,0,0,1]], dtype=np.float64)
                    # 必须设置精度,否则在替换矩阵列时会被自动圆整
b = np.matrix([[15], [24], [5]], dtype=np.float64)
c = np.matrix([2,1,0,0,0], dtype=np.float64)
baseInd = [2,3,4]

s=Simplex()
s.set_param( A, b, c, baseInd)
s.main()

Solve the list in this form, which satisfies the basic conditions of the simplex method, as follows:

================================================================================
          c_j           |  2.0   |  1.0   |  0.0   |  0.0   |  0.0   |
  c_B     x_B      b    |   x0   |   x1   |   x2   |   x3   |   x4   |
  0.0      x2     15.0  |  0.0   |  5.0   |  1.0   |  0.0   |  0.0   |
  0.0      x3     24.0  |  6.0   |  2.0   |  0.0   |  1.0   |  0.0   |
  0.0      x4     5.0   |  1.0   |  1.0   |  0.0   |  0.0   |  1.0   |
        c_j-z_j         |  2.0   |  1.0   |  0.0   |  0.0   |  0.0   |
         status         |         ...Iteration continues...          |
================================================================================
          c_j           |  2.0   |  1.0   |  0.0   |  0.0   |  0.0   |
  c_B     x_B      b    |   x0   |   x1   |   x2   |   x3   |   x4   |
  0.0      x2     15.0  |  0.0   |  5.0   |  1.0   |  0.0   |  0.0   |
  2.0      x0     4.0   |  1.0   |  0.33  |  0.0   |  0.17  |  0.0   |
  0.0      x4     1.0   |  0.0   |  0.67  |  0.0   | -0.17  |  1.0   |
        c_j-z_j         |  0.0   |  0.33  |  0.0   | -0.33  |  0.0   |
         status         |         ...Iteration continues...          |
================================================================================
          c_j           |  2.0   |  1.0   |  0.0   |  0.0   |  0.0   |
  c_B     x_B      b    |   x0   |   x1   |   x2   |   x3   |   x4   |
  0.0      x2     7.5   |  0.0   |  0.0   |  1.0   |  1.25  |  -7.5  |
  2.0      x0     3.5   |  1.0   |  0.0   |  0.0   |  0.25  |  -0.5  |
  1.0      x1     1.5   |  0.0   |  1.0   |  0.0   | -0.25  |  1.5   |
        c_j-z_j         |  0.0   |  0.0   |  0.0   | -0.25  |  -0.5  |
         status         |          Unique Optimal Solution           |
        objctive        |                    8.5                     |
        solution        |  3.5   |  1.5   |  7.5   |   0    |   0    |

Optimal solution:{X}^{*}=(3.5,1.5,7.5,0,0)^{T}

The optimal value:z ^ {*} = 8.5

Further discussion of the simplex method:

1. Artificial variable method (big M method)

After some linear programming problems are transformed into canonical form, there is no identity matrix in the constraint coefficient matrix, so it is necessary to add artificial variables to the constraints to construct a new linear programming problem. Taking the coefficient of the artificial variable in the objective function to an arbitrarily large negative number can prevent the artificial variable from appearing in the optimal solution during the solution process. If all test numbers in the final simplex table are less than or equal to zero, but there are still artificial variables that are not zero in the base variables, the problem is unsolved. Next, the calculation steps of the artificial variable method are given.

2. Two-stage method

Use the big M method to deal with artificial variables. When solving with an electronic computer, only a number of the maximum word length of the machine can be input into the computer for M. If the parameter value in the linear programming problem is close to the number representing M, or is far smaller than this number, the calculation result may be wrong due to the error in the value of the computer during calculation.

In order to overcome this difficulty, the linear programming problem after adding artificial variables can be calculated in two stages, which is called the two-stage method.

Learn more: Operations Research Issue No. 16 | Linear programming hard core knowledge points combing - simplex method - Zhihu

4.4.2 Dual Simplex Method:

Example: Solve LP using the dual simplex method:

\begin{aligned} &\operatorname{Min} W=2 x_{1}+3 x_{2}+4 x_{3} \\ &\text { s.t. }\left\{\begin{array}{c} x_{1}+2 x_{2}+x_{3} \geq 3 \\ 2 x_{1}-x_{2}+3 x_{3} \geq 4 \\ x_{1}, x_{2}, x_{3} \geq 0 \end{array}\right. \end{aligned}

(1) Standardization:

\begin{aligned} &\operatorname{Max} z=-2 x_{1}-3 x_{2}-4 x_{3} \\ &\text { s.t. }\left\{\begin{array}{c} x_{1}+2 x_{2}+x_{3}-x_{4}=3 \\ 2 x_{1}-x_{2}+3 x_{3}-x_{5}=4 \\ x_{1}, x_{2}, x_{3}, x_{4}, x_{5} \geq 0 \end{array}\right. \end{aligned}

Multiplying both sides of the two equality constraints by -1, we get

\begin{aligned} &\operatorname{Max}z=-2 x_{1}-3 x_{2}-4 x_{3} \\ &\text { s.t. }\left\{\begin{array}{c} -x_{1}-2 x_{2}-x_{3}+x_{4}=-3 \\ -2 x_{1}+x_{2}-3 x_{3}+x_{5}=-4 \\ x_{1}, x_{2}, x_{3}, x_{4}, x_{5} \geq 0 \end{array}\right. \end{aligned}

(2) Use python programming:

import re
import pandas as pd
from fractions import Fraction

# data = pd.read_csv("data2.csv", header=0, index_col=0)
# data=pd.DataFrame({"x1":[-2,-2,-1,-9],"x2":[-2,-3,-1,-12],"x3":[-1,-1,-5,-15],"x4":[1,0,0,0],"x5":[0,1,0,0],"x6":[0,0,1,0],"b":[-10,-12,-14,0]},index = ['x4', 'x5', 'x6',"sigma"])
data=pd.DataFrame({"x1":[-1,-2,-2],"x2":[-2,1,-3],"x3":[-1,-3,-4],"x4":[1,0,0],"x5":[0,1,0],"b":[-3,-4,0]},index = ['x4', 'x5' ,"sigma"])
print("=======原始表=======")

print(data)


def do_it(data):
    in_base_index = data.loc["sigma", :][data.loc['sigma', :] < 0].index
    rec = {}
    res_val = []
    for i in in_base_index:
        out_base_index = data.loc[:, i][data.loc[:, i] < 0].index.drop("sigma")
        for j in out_base_index:
            res = Fraction(data.loc["sigma", i], data.loc[j, i])
            res_val.append(res)
            rec[str(j) + "," + str(i)] = res

    def get_key(dic, value):
        return [k for k, v in dic.items() if v == value]

    s = get_key(rec, min(res_val))
    s = re.split(r"\,", s[0])

    # 这里是将本身变成1
    param1 = Fraction(1, data.loc[s[0], s[1]])
    data.loc[s[0], :] = data.loc[s[0], :] * param1
    # 将其他变为0
    for row in data.index.drop(s[0]):
        target = data.loc[row, s[1]]
        param2 = Fraction(-target, 1)
        data.loc[row, :] = data.loc[s[0], :] * param2 + data.loc[row, :]

    data = data.rename(index={s[0]: s[1]})
    print("================================")
    print(data)
    if (data["b"].drop("sigma") < 0).any():
        print("需要继续迭代!")
        do_it(data)
    else:
        print("迭代结束!")
    return data


# 如何b中的任何一个数小于零,都需要进一步操作
if (data["b"].drop("sigma") < 0).any():
    print("需要继续迭代!")
    do_it(data)
else:
    print("迭代结束!")

Solve the list in this form, which satisfies the basic conditions of the dual simplex method, as follows:

=======原始表=======
       x1  x2  x3  x4  x5  b
x4     -1  -2  -1   1   0 -3
x5     -2   1  -3   0   1 -4
sigma  -2  -3  -4   0   0  0
需要继续迭代!
================================
      x1    x2   x3 x4    x5   b
x4     0  -5/2  1/2  1  -1/2  -1
x1     1  -1/2  3/2  0  -1/2   2
sigma  0    -4   -1  0    -1   4
需要继续迭代!
================================
      x1 x2    x3    x4    x5     b
x2     0  1  -1/5  -2/5   1/5   2/5
x1     1  0   7/5  -1/5  -2/5  11/5
sigma  0  0  -9/5  -8/5  -1/5  28/5
迭代结束!

Optimal solution:{X}^{*}=(11 / 5,2 / 5,0,0,0) ^{T}

The optimal value:\begin{aligned} &\operatorname{minW}=-\max { }^{*}= -[11 / 5 \times(-2)+2 / 5 \times(-3)]=28 / 5 \end{aligned}
 

Guess you like

Origin blog.csdn.net/qq_21402983/article/details/126313619