Python-单纯形法(大M法)求解 直接求解、借助scipy包

目录

    • 1、直接算法
    • 2、借助scipy库

在线性规划问题的约束条件中加人工变量后,要求在目标函数中相应地添加认为的M或一M为系数的项。在极大化问题中,对人工变量赋于一M作为其系数;在极小化问题中,对人工变量赋于一个M作为其系数,M为一任意大(而非无穷大)的正数。把M看作一个代数符号参与运算,是单纯形法求解的一种。
详细算法可参看小编的另一篇博客,Excel-单纯形法(大M法)求解 直接求解与规划求解功能。

下列代码的原题目如下所示: max 2x1+x2+x3
Python-单纯形法(大M法)求解 直接求解、借助scipy包_第1张图片

1、直接算法

代码如下所示:
如果没有安装numpy库的同学,请先 使用命令pip intall numpy安装。

# encoding=utf-8
import numpy as np  # python 矩阵操作lib
class Simplex():

    def __init__(self):
        self._A = ""  # 系数矩阵
        self._b = ""  #数组
        self._c = ''  # 约束
        self._B = ''  # 基变量的下标集合
        self.row = 0  # 约束个数

    def solve(self):
        # 读取文件内容,文件结构前两行分别为 变量数 和 约束条件个数
        # 接下来是系数矩阵
        # 然后是b数组
        # 然后是约束条件c
        # 假设线性规划形式是标准形式(都是等式)
        A = []
        b = []
        c = []
        self._A = np.array(A, dtype=float)
        self._b = np.array(b, dtype=float)
        self._c = np.array(c, dtype=float)
        self._A = np.array([[0,2,-1],[0,1,-1]],dtype=float)
        self._b = np.array([-2,1],dtype=float)
        self._A = np.array([[1,-1,1]]) # 等式约束系数self._A,3x1维列向量
        self._b = np.array([2]) # 等式约束系数self._b,1x1数值       
        self._c = np.array([2,1,1],dtype=float)
        self._B = []
        self.row = len(self._b)
        self.var = len(self._c)
        (x, obj) = self.Simplex(self._A, self._b, self._c)
        self.pprint(x, obj, A)

    def pprint(self, x, obj, A):
        px = ['x%d = %f' % (i + 1, x[i]) for i in range(len(x))]
        print(','.join(px))
        print('objective value is : %f' % obj)
        for i in range(len(A)):
            print('%d-th line constraint value is : %f' % (i + 1, x.dot(A[i])))

    def InitializeSimplex(self, A, b):
        b_min, min_pos = (np.min(b), np.argmin(b))  # 得到最小bi
        # 将bi全部转化成正数
        if (b_min < 0):
            for i in range(self.row):
                if i != min_pos:
                    A[i] = A[i] - A[min_pos]
                    b[i] = b[i] - b[min_pos]
            A[min_pos] = A[min_pos] * -1
            b[min_pos] = b[min_pos] * -1
        # 添加松弛变量
        slacks = np.eye(self.row)
        A = np.concatenate((A, slacks), axis=1)
        c = np.concatenate((np.zeros(self.var), np.ones(self.row)), axis=0)
        # 松弛变量全部加入基,初始解为b
        new_B = [i + self.var for i in range(self.row)]
        # 辅助方程的目标函数值
        obj = np.sum(b)
        c = c[new_B].reshape(1, -1).dot(A) - c
        c = c[0]
        # entering basis
        e = np.argmax(c)
        while c[e] > 0:
            theta = []
            for i in range(len(b)):
                if A[i][e] > 0:
                    theta.append(b[i] / A[i][e])
                else:
                    theta.append(float("inf"))
            l = np.argmin(np.array(theta))
            if theta[l] == float('inf'):
                print('unbounded')
                return False
            (new_B, A, b, c, obj) = self._PIVOT(new_B, A, b, c, obj, l, e)
            e = np.argmax(c)
        # 如果此时人工变量仍在基中,用原变量去替换之
        for mb in new_B:
            if mb >= self.var:
                row = mb - self.var
                i = 0
                while A[row][i] == 0 and i < self.var:
                    i += 1
                (new_B, A, b, c, obj) = self._PIVOT(new_B, A, b, c, obj, new_B.index(mb), i)
        return (new_B, A[:, 0:self.var], b)

    # 算法入口
    def Simplex(self, A, b, c):
        B = ''
        (B, A, b) = self.InitializeSimplex(A, b)
        # 函数目标值
        obj = np.dot(c[B], b)
        c = np.dot(c[B].reshape(1, -1), A) - c
        c = c[0]
        # entering basis
        e = np.argmax(c)
        # 找到最大的检验数,如果大于0,则目标函数可以优化
        while c[e] > 0:
            theta = []
            for i in range(len(b)):
                if A[i][e] > 0:
                    theta.append(b[i] / A[i][e])
                else:
                    theta.append(float("inf"))
            l = np.argmin(np.array(theta))
            if theta[l] == float('inf'):
                print("unbounded")
                return False
            (B, A, b, c, obj) = self._PIVOT(B, A, b, c, obj, l, e)
            e = np.argmax(c)
        x = self._CalculateX(B, A, b, c)
        return (x, obj)
    
    # 得到完整解
    def _CalculateX(self, B, A, b, c):
        x = np.zeros(self.var, dtype=float)
        x[B] = b
        return x

    # 基变换
    def _PIVOT(self, B, A, b, c, z, l, e):
        main_elem = A[l][e]
        A[l] = A[l] / main_elem
        b[l] = b[l] / main_elem
        for i in range(self.row):
            if i != l:
                b[i] = b[i] - A[i][e] * b[l]
                A[i] = A[i] - A[i][e] * A[l]
        z -= b[l] * c[e]
        c = c - c[e] * A[l]
        B[l] = e
        return (B, A, b, c, z)

s = Simplex()
s.solve()

结果如下图所示:
可以看到,x1=0,x2=0,x3=2,max=2。与之前excel运算出来的一致。
在这里插入图片描述

2、借助scipy库

跟上面是同一个例子,为了方便比较再贴一张上来。
Python-单纯形法(大M法)求解 直接求解、借助scipy包_第2张图片
代码如下:

import numpy as np
from scipy import optimize as op

# 给出变量取值范围
x1=(0,None)
x2=(0,None)
x3=(0,None)

c = np.array([2,1,1])# 目标函数系数
A_ub = np.array([[0,2,-1],[0,1,-1]]) # 不等式1和3系数,要注意第一个式子1系数的负数
B_ub = np.array([-2,1])# 式子1和式子3的约束系数
A_eq = np.array([[1,-1,1]])# 式子2的系数
B_eq = np.array([2])# 式子2的约束系数

res=op.linprog(c,A_ub,B_ub,A_eq,B_eq,bounds=(x1,x2,x3))#调用函数进行求解
print(res) #fun就是目标函数最小值,x就是最优解

运行结果如下:
可以看到结果非常非常接近,但是我们的结果没有那么直接。这个应该是python版本的计算精度不同造成的,如果你的python是3.5版本的,运算出来应该就是x1=0,x2=0,x3=2,max=2。
Python-单纯形法(大M法)求解 直接求解、借助scipy包_第3张图片

如果我们去掉代码里的变量取值范围这三行代码,可以看到准确度稍不如前者。但粗略看起来的结果还是一致的。
运行结果如下:
Python-单纯形法(大M法)求解 直接求解、借助scipy包_第4张图片

参考教程:https://blog.csdn.net/qq_40707407/article/details/81709122

你可能感兴趣的:(Python,人工智能与机器学习)