线性规划问题,在线性等式或者不等式的约束下,去求解一个线性目标函数的最大值最小值问题。
首先想到的是scipy中的优化包→optimize里面的 linprog。这个名字和MATLAB里面优化的名字是一样的。
对于简单的连续性线性极值问题,可以使用。
from scipy import optimize as op
help(op.linprog)
查看帮助,可以看到这个函数的用法
linprog(c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
method='interior-point', callback=None, options=None, x0=None)
最主要的就是 c,A_ub, b_ub, A_eq, b_eq,前五个参数。
这个函数求的是最小值,也就是求c(目标线性函数)最小,如果现实是求极大值,那么要加一个负号。
他所有的不等式约束都是默认"<=" , 所以要把所有的不等式都转成小于
A_ub,二维数组,所有不等式约束的系数组成
b_ub,一维数组,所有不等式约束的结果组成
A_eq,二维数组,就是约束中等式的系数组成
b_eq,一维数组,所以约束中等式的结果组成
bounds,边界,就是结果的取值边界,默认是(0,None),就是0到无穷大。可以写多个,比如有三个未知数:((0,3),(0,100),(0,None))。
method,方法,{‘interior-point’, ‘revised simplex’, ‘simplex’}三个值可选。
例:
设变量x1为A1牛奶的产量,x2为A2的
目标函数就是利益最大:24 * x1+16 * x2 (也就是最小化: -24 * x1-16 * x2)
约束条件为:
x1<=100
4 * x1 + 2 * x2 <=480
x1 / 3 + x2 / 4<=50
x1,x2 >=0
由于里面有除号,所以计算机会近似,因为精度问题,得到的结果不理想。所以建模的时候不要有除号的出现。
我们重新定义,把产量前面加上系数:
设变量3 * x1为A1牛奶的产量,4 * x2为A2的
目标函数就是利益最大:24 * (3 * x1)+16 * (4 * x2) (也就是最小化: -72 * x1 -64 * x2)
约束条件为:
3 * x1<=100
4 * (3 * x1) + 2 * (4 * x2) <=480
x1 + x2 <=50
x1,x2 >=0
代码:(注意,如果式子中没有出现的未知数系数要写0,不能不写)
c = np.array([-72,-64])
A_ub = np.array([[3,0],[12,8],[1,1]])
b_ub = np.array([100,480,50])
op.linprog(c,A_ub,b_ub,method='revised simplex')
结果:
con: array([], dtype=float64)
fun: -3360.0
message: 'Optimization terminated successfully.'
nit: 4
slack: array([40., 0., 0.])
status: 0
success: True
x: array([20., 30.])
A1产量60公斤,A2产量120公斤,最大利润3360
注意:要看status和success。不管成功与否,x都会给一个值。但是不一定能用。
只有 status: 0(表示算法状态状态码,0表示最优化名义上有解,是可行的), success: True(当算法成功完成时)
slack:松弛度,表示的是得到的结果集,带入原不等式,得到的差距有多少。这里可以看出第二个和第三个不等式的差距为0,说明时间和牛奶量都被充分利用了。
另外一个例子:
这是一个投资问题,学经济学的一般都会讲建模。
这里一共四个投资方案,每隔投资方法应该投多少,才能达到5年末总获利是最高的?
见下表,每个方案,投资的数,我用abcd表示
第一年 | 第二年 | 第三年 | 第四年 | 第五年 | |
---|---|---|---|---|---|
方案A | a1 | a2 | a3 | a4 | |
方案B | b | ||||
方案C | c | ||||
方案D | d1 | d2 | d3 | d4 | d5 |
那么就可以得出5年末的时候获利的金额是:
1.15 * a4 + 1.25 * b + 1.4 * c + 1.11 * d5
这就是目标函数,使他最大化。
因为第五年得到的,就是你前面投资产生的本利。第一年只跟a4有关系,以此类推
注意,方案A的获利在次年末。方案D的获利在每年初。所以第一年投的钱,第二年只能拿到d1产生的本利,再用这个钱去投资其他方案。以此类推,得到以下的约束条件:
a1 + d1 <=10
a2+c1+d2 = 1.11d1
a3+b1+d3 = 1.15a1+1.11d2
a4+d4 = 1.15a2+1.11d3
d5 = 1.15a3 + 1.11d4
这些式子化简转到一边。
代码如下:
c = np.array([0,0,0,-1.15,-1.25,-1.4,0,0,0,0,-1.11])
A_ub = np.array([[1,0,0,0,0,0,1,0,0,0,0],
[0,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0]])
b_ub = np.array([10,4,3])
A_eq = np.array([
[0,1,0,0,0,1,-1.11,1,0,0,0],
[1.15,0,-1,0,-1,0,0,1.11,-1,0,0],
[0,1.15,0,-1,0,0,0,0,1.11,-1,0],
[0,0,1.15,0,0,0,0,0,0,1.11,-1]
])
b_eq = np.array([0,0,0,0])
op.linprog(c,A_ub,b_ub,A_eq,b_eq,method='revised simplex')
结果:
con: array([0.00000000e+00, 0.00000000e+00, 1.77635684e-15, 0.00000000e+00])
fun: -16.85058155100001
message: 'Optimization terminated successfully.'
nit: 8
slack: array([1.77635684e-15, 4.00000000e+00, 3.00000000e+00])
status: 0
success: True
x: array([ 0. , 0. , 0. , 0. , 0. ,
0. , 10. , 11.1 , 12.321 , 13.67631 ,
15.1807041])
整数规划:
例:
对于下面的式子
c = [3,4,1]
A = [[-1,-6,-2],[-2,0,0]]
b = [-5,-3]
Aeq = [[0,0,0]]
beq = [0]
res = op.linprog(c, A, b, Aeq, beq,method="revised simplex")
求解结果
con: array([0.])
fun: 6.25
message: 'Optimization terminated successfully.'
nit: 0
slack: array([0., 0.])
status: 0
success: True
x: array([1.5 , 0. , 1.75])
如果要求是整数,就无能为力。或者可以用lingo求解。这里不赘述。
在Python里有一个pulp的库,可以用来解整数问题
pulp在Python里本身不自带,要pip install pulp
import pulp as pp
# 定义一个问题,第一个参数就是一个名字,随便取,后面是一个int型,说明的是求最大或者最小
# pp.LpMinimize其实就是一个int,值为1
# pp.LpMaximize也是一个int,值为-1.
# 在参数后面也可以直接写1或者-1表示
# 其实默认的都是求最小值,在求最大值的时候两边都要乘以-1,转成最小值求解。
# 所以最小值参数为1,最大值参数为-1
mylp = pp.LpProblem("lp1",pp.LpMinimize)
# 定义未知数,标记取值范围,cat为限制条件,Integer表示整数型,Continuous表示连续型
x1 = pp.LpVariable("x1",lowBound=0,cat="Integer")
x2 = pp.LpVariable("x2",lowBound=0,cat="Integer")
x3 = pp.LpVariable("x3",lowBound=0,cat="Integer")
# 在pulp中用+=符号,加约束和目标函数
# 只支持 = ,>= , <= 不支持> , <
mylp += 3*x1+4*x2+x3
mylp +=(x1+6*x2+2*x3 >=5)
mylp +=(2*x1 >=3)
打印mylp输出如下:
lp1:
MINIMIZE
3*x1 + 4*x2 + 1*x3 + 0
SUBJECT TO
_C1: x1 + 6 x2 + 2 x3 >= 5
_C2: 2 x1 >= 3
VARIABLES
0 <= x1 Integer
0 <= x2 Integer
0 <= x3 Integer
# 最后一定要调用这个函数。才能得到解
# 他返回一个int型,是一个status,表示的是pp.LpStatus里面对应的数字含义
# {0: 'Not Solved',
# 1: 'Optimal',最佳的
# -1: 'Infeasible',
# -2: 'Unbounded',
# -3: 'Undefined'}
i = mylp.solve()
print(i) # 结果是1,返回1就说明成功计算了
# 得到这些值
[v.varValue for v in mylp.variables()]
输出:
[2.0, 0.0, 2.0]
mylp.objective
输出:3*x1 + 4*x2 + 1*x3 + 0
pp.value(mylp.objective)
最终的结果表达式的值,值为8.0
如果很复杂,还可以通过字典的形式,添加,这网上有很多教程,不赘述,有机会再写。