这里最优化问题的讨论,主要指在给定某个确认的目标函数以及该函数的自变量的一些约束条件,求函数的最大或最小值的问题,通用的数学表达式:
根据约束条件以及目标函数性质不同,最优化问题求解的思路也有很大的不同,其中无约束优化问题的方法是基础,而带约束优化问题则在一定条件下可以转化为无约束优化问题来求解。
凸函数 (convex function) [1] 定义:
f(t∗x1+(1−t)∗x2)≤t∗f(x1)+(1−t)∗f(x2)
x1,x2∈X,t∈[0,1]
当优化问题满足以下条件时,
1) 线性目标函数 (linear objective function)
2) 线性约束条件 (linear constraints)
3) 自变量大于零
我们称该优化问题为线性优化问题(LP - Linear Programming) , 数学表达式为[3]:
在这我们更关注如何通过计算程序/代码解决线性优化问题, 主流思路是单纯形算法 (Simplex Algorithm) [4] [George Dantzig, 1947], 该算法的基本思路是,
1) 为待求解目标函数和约束条件添加宽松变量(slack varibles),使其成为等式
2) 设定 m个 (m < n, n 为待求解自变量)主成分变量 (basis)
3) 将n-m个非主成分变量设置为零,解出m个主成分值
4) 从该m个主成分极值(extreme point) 移到下一个主成分极值
5) 最优解为可行域中的极值点(extreme point)
具体例子, 膳食问题 (The Diet Problem) 表述已知人体所需元素列表
1) 目标函数:
问题具体化 (例子中的数据没有任何实际意义,可以为任何整数),假设人体所需元素有钙(480),锌(160),钠(1190), 市场上含有该元素的材料有红豆 (单价13),猪肉(单价23), 其中每种单位材料对应元素含量见下表:
红豆 | 猪肉 | |
---|---|---|
钙 | 5 | 15 |
锌 | 4 | 4 |
钠 | 35 | 20 |
1) 目标函数
Step Two - 设置主成分变量,初始化时以宽松变量为主成分,即a, b, c, 并初始化非主成分元素为0, x=0, y=0, z=0, 求解得a,b,c分别为480, 160, 1190,该点一定为可行域极点;
Step Three - 随机选取待求解等式中的非主成分变量{x, y},如y, 后将y作为主成分成员,移除任意一个主成分成员{a, b, c}, 如a, 非主成分x, a为0,主成分变量y=32, b=32, c=550, z=736
Step Four - 重复主成分替换过程,将x移入主成分, {x, y, b},非主成分 {a, c, z}=0, 得x=12, y=28, z=800, c=110;
步骤 | 主成分 | 非主成分 |
---|---|---|
Step One | a=480,b=160,c=1190 | x,y=0 |
Step Two | y=32,b=32,c=550 | x,a=0 |
Step Three | x=12,y=28,c=110 | a,b=0 |
代码部分:
# -*- encoding: utf-8 -*-
import re, sys
import numpy as np
import copy
class SimplexAlgorithm(object):
"""
Implement of Simplex Algorithm
"""
def __init__(self, A, b, c):
"""
A = constraint Matrix
I = slack Matrix
b = constraint
c = objective coefficient
"""
A = np.array(A)
b = np.array(b)
c = np.array(c)
assert len(A.shape) == 2
assert A.shape[0] == len(b)
assert A.shape[1] == len(c)
M = A.shape[0]
N = len(c)
a = []
for i in range(M):
_temp = []
for j in range(N):
_temp.append(A[i, j])
# I part
_I = [0] * M
_I[i] = 1.
# b Part
_b = [b[i]]
_temp += _I + _b
a.append(_temp)
# c part
a.append(list(c) + [0] * (M + 1))
print "Construct Matrix: "
for ele in a:
print "\t".join([str(ent) for ent in ele])
print "---------"
self.matrix = a
self.N = N
self.M = M
def pivot(self, x, y):
"""
"""
for i in range(self.M + 1):
for j in range(self.M + self.N + 1):
if (x != i and j != y):
self.matrix[i][j] -= self.matrix[x][j] * self.matrix[i][y] / self.matrix[x][y]
# zero out column y
for i in range(self.M + 1):
if (i != x): self.matrix[i][y] = 0.0
# scale row x
for j in range(self.M + self.N + 1):
if (j != y): self.matrix[x][j] /= self.matrix[x][y]
self.matrix[x][y] = 1.
def solve(self):
"""
"""
while True:
p, q = 0, 0
for q in range(self.M + self.N + 1):
if (self.matrix[self.M][q] > 0): break # positive objective coefficient
if (q >= self.M + self.N): break
for p in range(self.M):
if self.matrix[p][q] > 0: break
for i in range(p+1, self.M):
if self.matrix[i][q] > 0:
# min ratio test
if (self.matrix[i][self.M + self.N] / self.matrix[i][q] <
self.matrix[p][self.M + self.N] / self.matrix[p][q]):
p = i
self.pivot(p, q)
return self.matrix
if __name__ == "__main__":
reload(sys)
sys.setdefaultencoding("utf-8")
# value should be float type
A = [[5., 15.], [4., 4.], [35., 20.]]
c = [13., 23.]
b = [480., 160., 1190.]
instance = SimplexAlgorithm(A, b, c)
res = instance.solve()
print "Result: "
for ele in res:
print "\t".join([str(ent) for ent in ele])
1 https://en.wikipedia.org/wiki/Convex_function
2 https://www.cs.princeton.edu/~rs/AlgsDS07/22LinearProgramming.pdf
3 https://en.wikipedia.org/wiki/Linear_programming
4 https://en.wikipedia.org/wiki/Simplex_algorithm