本文介绍如何用数学语言对实际中的优化问题进行建模. 通过建立数学模型, 我们利用现成的求解器可以便捷地计算出最优解(或可行解).

运输问题

考虑三个粮食储量分别是100, 200, 300的仓库 (单位:吨, 下文省略). 我们需要把粮食运送给4个客户, 其需求分别是: 120, 60, 270, 150.

仓库到客户的单位运输成本用矩阵CCC描述:
[350200300250220330300270215230290240]\begin{aligned} \begin{bmatrix} 350 & 200 & 300 & 250 \\ 220 & 330 & 300 & 270 \\ 215 & 230 & 290 & 240 \\ \end{bmatrix} \end{aligned} ⎣⎡​350220215​200330230​300300290​250270240​⎦⎤​​
其中行代表仓库, 列代表客户. 矩阵中的每一个值代表对应的仓库到客户的单位运输成本. 我们的目标最小化总的运输成本.

下面我们用数学语言描述该问题.

输入

  • 仓库的供给量sis_isi​, i=1,2,...,mi=1, 2, ... ,mi=1,2,...,m, 其中mmm是仓库总数
  • 客户的需求量djd_jdj​, j=1,2,...,nj= 1, 2, ..., nj=1,2,...,n, 其中nnn是客户总数
  • 仓库iii到客户jjj的单位运输成本是ci,jc_{i, j}ci,j​

输出

  • 需要计算仓库iii到客户jjj的运输量xi,jx_{i,j}xi,j​

下面我们写出问题的目标和约束.

目标是最小化总的运输成本, 即
min⁡∑i,jcijxij.\min \sum_{i,j}c_{ij}x_{ij}.mini,j∑​cij​xij​.

我们需要满足的约束条件有两个:

  • 每个仓库的出库量不能超过其供给量: ∑jxij≤ai\sum_{j} x_{ij} \leq a_i∑j​xij​≤ai​, ∀i\forall i∀i
  • 每个客户的需求应该被满足: ∑ixij=dj\sum_{i} x_{ij} = d_j∑i​xij​=dj​, ∀j\forall j∀j

综上所述, 我们可以把运输问题用线性规划(Linear Programming)来表示.

min⁡∑ijcijxijs.t. ∑jxij≤ai,∀i∑ixij=dj,∀jxij≥0,∀i,j.\begin{aligned} \min~& \sum_{ij}c_{ij} x_{ij} \\ \text{s.t. } & \sum_{j} x_{ij} \leq a_i, \forall i \\ & \sum_{i} x_{ij} = d_j, \forall j \\ & x_{ij} \geq 0, \forall i, j. \end{aligned} min s.t. ​ij∑​cij​xij​j∑​xij​≤ai​,∀ii∑​xij​=dj​,∀jxij​≥0,∀i,j.​

标准实践

为了更加直观地写出数学模型, 我们可以总结一份标准的指南. 它包含四个基本步骤:

  • 指标(Indices)
    指标的作用是主要为了简化记号. 以上述运输问题为例, 我们的指标有iii和jjj, 其中iii代表仓库, jjj代表客户.

  • 参数(Parameters)
    参数是问题的输入. 以上述运输问题为例, 我们的参数是: 供给量(sis_isi​), 需求量(dj)d_j)dj​), 单位运输成本ci,jc_{i,j}ci,j​.

  • 决策变量(Decision Variables)
    决策变量是算法的输出.

  • 优化目标(Objective)
    一般是最小化或最大化一个目标函数. 在某些情况下, 问题只需要找到一个可行解, 因此也可以不指定优化目标.

  • 约束(Constraints)
    用等式或不等式描述解的限制.

求解规划

常用的商用求解器有Gruobi和CPLEX(可申请教育和学术的lisense). 商用求解器功能强大, 能求解多种类型的规划问题, 例如整数规划, 混合整数规划, 二次规划等. 免费的求解器有Google的ORtools, 它把一些开源的求解器做了集成, 求解速度虽然比不上商用求解器, 实际中也能满足很多业务需求.

求解方式有两种:

第一种是直接用商用求解器提供的IDE. 按照求解器的建模语法把模型写出来, 然后求解. 建模语法的好处是非常贴近公式化的描述, 所见即所得.

第二种是调用求解器提供的API, 初始化参数, 约束, 目标, 然后求解.

本文我们使用开源工具ORtools求解(基本的教程请自行google,需要翻墙)

Python实现

模型

# model.pyfrom ortools.linear_solver import pywraplp
import numpy as npclass TransportModel(object):def __init__(self, a, d, C):""":param a: 供给量(m维向量), m代表仓库数量:param d: 需求量(n维向量), n代表客户数量:param C: 单位运输成本(m*n维矩阵), C[i][j]代表仓库i到客户j的单位运输成本"""self._solver = pywraplp.Solver('TransportModel',pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)self._a = aself._d = dself._C = Cself._m = len(self._a)  # 仓库数量self._n = len(self._d)  # 客户数量self._x = None  # 决策变量self._solution_x = None  # 计算结果self._obj_val = None  # 目标函数值def _init_decision_variables(self):self._x = [# 0 <= x[i][j] <= infinity[self._solver.NumVar(0, self._solver.infinity(), "x[%d][%d]" % (i, j))for j in range(self._n)] for i in range(self._m)]def _init_constraints(self):# 每个仓库的出库量不能超过其供给量# sum(x[i][j]) <= a[i], over jfor i in range(self._m):ct = self._solver.Constraint(0, self._a[i])for j in range(self._n):ct.SetCoefficient(self._x[i][j], 1)# 每个客户的需求应该被满足# sum(x[i][j]) == b[j], over ifor j in range(self._n):ct = self._solver.Constraint(self._d[j], self._d[j])for i in range(self._m):ct.SetCoefficient(self._x[i][j], 1)def _init_objective(self):obj = self._solver.Objective()for i in range(self._m):for j in range(self._n):obj.SetCoefficient(self._x[i][j], self._C[i][j])obj.SetMinimization()def solve(self):self._init_decision_variables()self._init_constraints()self._init_objective()self._solver.Solve()# 求解器返回的解self._solution_x = [[self._x[i][j].solution_value() for j in range(self._n)]for i in range(self._m)]# sum(C[i][i] * x[i][j]) over i,jself._obj_val = np.sum(np.array(self._C) * np.array(self._solution_x))def print_result(self):print("最优值 = ", self._obj_val)print("最优解 x = ")print(np.array(self._solution_x))

主函数

# main.pyfrom data import a, d, C  # 运输问题实例
from model import TransportModelif __name__ == '__main__':tm = TransportModel(a, d, C)tm.solve()tm.print_result()

完整代码: 运输问题

数独(Sudoku)

把数字1-9填入下图的空格子中, 且满足如下三个条件:

  1. 每个区块 (图中灰色方框包含的3$\times$3小格子)包含数字1-9
  2. 每行包含数字1-9
  3. 每列包含数字1-9

我们通过数学规划的方式求解该问题.

指标

  • nnn – 填入的数字, n∈{1,2,...,9}n \in \{1, 2, ..., 9 \}n∈{1,2,...,9}
  • iii – 第iii行区块, 区块一共三行, 因此i∈{1,2,3}i \in \{1, 2, 3\}i∈{1,2,3}
  • jjj – 第jjj行区块, 区块一共三列, 因此j∈{1,2,3}j \in \{1, 2, 3\}j∈{1,2,3}
  • ppp – 区块中元素的行, 每个区块包含三行, 因此$p \in {1,2,3 } $
  • qqq – 区块中元素的列, 每个区块包含三列q∈{1,2,3}q \in \{ 1, 2, 3\}q∈{1,2,3}

参数

  • ai,j,p,q,n∈{0,1}a_{i,j,p,q,n} \in \{ 0, 1\}ai,j,p,q,n​∈{0,1} – 考虑第iii行jjj列的区块, 它的iii行jjj列是否数字nnn

决策变量

  • xi,j,p,q,n∈{0,1}x_{i,j,p,q,n} \in \{ 0, 1\}xi,j,p,q,n​∈{0,1} – 考虑第iii行jjj列的区块, 它的iii行jjj列是填入否数字nnn

约束

  1. 已经存在的值不能修改.
    xi,j,p,q,n≥ai,j,p,q,nx_{i,j,p,q, n} \geq a_{i,j,p, q, n}xi,j,p,q,n​≥ai,j,p,q,n​, ∀i,j,p,q,n\forall i,j,p,q, n∀i,j,p,q,n
  2. 一个单元格同时只允许填入一个数字.
    ∑nxi,j,p,q,n=1,∀i,j,p,q\sum_n x_{i,j,p,q,n} = 1, \forall i,j,p,q∑n​xi,j,p,q,n​=1,∀i,j,p,q
  3. 每个区块包含数字1-9.
    ∑p,qxi,j,p,q,n=1,∀i,j,n\sum_{p, q} x_{i,j, p, q, n} = 1, \forall i, j, n∑p,q​xi,j,p,q,n​=1,∀i,j,n
  4. 每行包含数字1-9.
    ∑j,qxi,j,p,q,n=1,∀i,p,n\sum_{j, q} x_{i,j,p,q,n} = 1, \forall i,p, n∑j,q​xi,j,p,q,n​=1,∀i,p,n
  5. 每列包含数字1-9.
    ∑i,pxi,j,p,q,n=1,∀j,q,n\sum_{i, p} x_{i,j,p,q,n} = 1, \forall j,q, n∑i,p​xi,j,p,q,n​=1,∀j,q,n

综上所述, 我们的规划可以写成下面的整数规划(Integer Programming). 注意: 无优化目标.

min⁡0s.t. xi,j,p,q,n≥ai,j,p,q,n,∀i,j,p,q,n∑nxi,j,p,q,n=1,∀i,j,p,q∑p,qxi,j,p,q,n=1,∀i,j,n∑j,qxi,j,p,q,n=1,∀i,p,n∑i,pxi,j,p,q,n=1,∀j,q,nxi,j,p,q∈{0,1}.\begin{aligned} \min~& 0 \\ \text{s.t. } & x_{i,j,p,q,n} \geq a_{i,j,p,q,n}, \forall i, j,p,q, n \\ & \sum_n x_{i,j,p,q,n} = 1, \forall i,j,p,q \\ & \sum_{p, q} x_{i,j, p, q, n} = 1, \forall i, j, n \\ & \sum_{j, q} x_{i,j,p,q,n} = 1, \forall i,p, n \\ & \sum_{i, p} x_{i,j,p,q,n} = 1, \forall j,q, n \\ & x_{i,j,p,q} \in \{ 0,1\} . \end{aligned} min s.t. ​0xi,j,p,q,n​≥ai,j,p,q,n​,∀i,j,p,q,nn∑​xi,j,p,q,n​=1,∀i,j,p,qp,q∑​xi,j,p,q,n​=1,∀i,j,nj,q∑​xi,j,p,q,n​=1,∀i,p,ni,p∑​xi,j,p,q,n​=1,∀j,q,nxi,j,p,q​∈{0,1}.​

Python实现

模型

# model.pyfrom ortools.linear_solver import pywraplp
import numpy as npclass SudokuModel(object):def __init__(self, a):""":param a: Sudoku实例 """self._solver = pywraplp.Solver('SudokuModel',pywraplp.Solver.BOP_INTEGER_PROGRAMMING)self._a = aself._x = None  # 决策变量self._solution_x = None  # 计算结果def __init_decision_variables(self):self._x = np.empty((3, 3, 3, 3, 9)).tolist()for i in range(3):for j in range(3):for p in range(3):for q in range(3):for n in range(9):# 已知数字不允许修改# x[i][j][p][q][n] >= a[i][j][p][q][n]self._x[i][j][p][q][n] \= self._solver.IntVar(self._a[i][j][p][q][n], 1,'x[%d][%d][%d][%d][%d]' % (i, j, p, q, n))def __init_constraints(self):# 一个单元格同时只允许填入一个数字# sum(x[i][j][p][q][n]) = 1, over nfor i in range(3):for j in range(3):for p in range(3):for q in range(3):ct = self._solver.Constraint(1, 1)for n in range(9):ct.SetCoefficient(self._x[i][j][p][q][n], 1)# 每个区块包含数字1-9# sum(x[i][j][p][q][n]) = 1, over p, qfor i in range(3):for j in range(3):for n in range(9):ct = self._solver.Constraint(1, 1)for p in range(3):for q in range(3):ct.SetCoefficient(self._x[i][j][p][q][n], 1)# 每行包含数字1-9# sum(x[i][j][p][q][n]) = 1, over j, qfor i in range(3):for p in range(3):for n in range(9):ct = self._solver.Constraint(1, 1)for j in range(3):for q in range(3):ct.SetCoefficient(self._x[i][j][p][q][n], 1)# 每列包含数字1-9# sum(x[i][j][p][q][n]) = 1, over i, pfor j in range(3):for q in range(3):for n in range(9):ct = self._solver.Constraint(1, 1)for i in range(3):for p in range(3):ct.SetCoefficient(self._x[i][j][p][q][n], 1)def solve(self):self.__init_decision_variables()self.__init_constraints()self._solver.Solve()self._get_solution_x()def _get_solution_x(self):self._solution_x = np.empty((3, 3, 3, 3))for i in range(3):for j in range(3):for p in range(3):for q in range(3):for n in range(9):if self._x[i][j][p][q][n].solution_value() == 1:self._solution_x[i][j][p][q] = n + 1def print_result(self):res = np.empty((9, 9))for i in range(3):for p in range(3):for j in range(3):for q in range(3):res[i*3+p][j*3+q] = self._solution_x[i][j][p][q]print(res)

主函数

# main.pyfrom model import SudokuModel
from data import aif __name__ == '__main__':sm = SudokuModel(a)sm.solve()sm.print_result()

完整代码: Sudoku

用数学规划的方式求解优化问题相关推荐

  1. MAT之PSO:利用PSO+ω参数实现对一元函数y = sin(10*pi*x) ./ x进行求解优化,找到最优个体适应度

    MAT之PSO:利用PSO+ω参数实现对一元函数y = sin(10*pi*x) ./ x进行求解优化,找到最优个体适应度 目录 输出结果 实现代码 输出结果 实现代码 x = 1:0.01:2; y ...

  2. MAT之PSO:利用PSO实现对一元函数y = sin(10*pi*x) ./ x进行求解优化,找到最优个体适应度

    MAT之PSO:利用PSO实现对一元函数y = sin(10*pi*x) ./ x进行求解优化,找到最优个体适应度 目录 输出结果 代码设计 输出结果 代码设计 x = 1:0.01:2; y = s ...

  3. python实现单例模式的几种方式_基于Python中单例模式的几种实现方式及优化详解...

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  4. 动态规划旅游问题:汽车加满油可以跑n千米,中途有若干个加油站,请用动态规划的方式求解中途加油次数最少的方案。

    算法课的课堂测试,问题大概就是这些描述. 动态规划旅游问题: 汽车加满油可以跑n千米,中途有若干个加油站,请用动态规划的方式求解中途加油次数最少的方案. 我的代码: #include<iostr ...

  5. 快速排序三种实现方式及其优化

    快速排序三种实现方式及其优化 1.关于快速排序 快速排序是分治法的一个应用. 根据分治法的思想,快速排序算法可描述为: 分解∶数组A[p-r]被划分为两个子数组A[p-q-1]和A[q+1,r],使得 ...

  6. STM32与DS18B20数字温度传感器寄生供电方式的优化方案与1-wire总线程序设计

    STM32与DS18B20数字温度传感器寄生供电方式的优化方案与1-wire总线程序设计 DS18B20是常用的一种数字温度传感器,通过1-wire总线实现传感器内部寄存器的访问.传感器有两种供电方式 ...

  7. python求解优化问题_科学计算:最优化问题-2(Python)

    三.最小二乘法 最小二乘法(least square method)是一种数学优化技术.它通过最小化误差的平方和寻找数据的最佳函数匹配.利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实 ...

  8. 实验一.MATLAB求解优化问题

    1.作出下列函数图形,观察所有的局部极大.极小和全局最大.最小值点的粗略位置,并用MATLAB函数fminbnd和fminsearch求各极值点的确切位置. 1)作图: syms x y1=x^2*s ...

  9. matlab使用CVX求解优化问题时,如果变量搜索空间过大,导致求解的数值解相当不准确,通过变量替换,缩小搜索空间

    错误的做法 clear clc rand('seed',1); %% 参数初始化 I = 5; N = 5; q = ones(1,I)/I; theta = [11,13,15,18,21]/10; ...

最新文章

  1. Nat. Commun. | 条件GAN网络和基因表达特征用于类苗头化合物的发现
  2. go 指针变量和普通变量的转化_7.8 C++指针变量的引用
  3. python 温度 符号_Python通过小实例入门学习---1.0(温度转换)
  4. Atitit.js this错误指向window的解决方案
  5. Golang的聊天服务器实践(群聊,广播)(一)
  6. android开发之局域网内屏幕共享+提取文字01:截屏
  7. 如何在html页面跳转的时候携带数据(页面跳转时参数传递问题)?
  8. MATLAB 插值函数运用 - interp1
  9. 杭州电子科技大学计算机学硕复试,2019杭州电子科技大学计算机软件考研复试手册.docx...
  10. mysql按分号拆分_mysql分号分割开字段。拆分
  11. Swagger Error Missing required property: responses ✖ Swagger Error Additional properties not allowe
  12. cadence lux介绍_Cadence软件介绍
  13. 深入探索 Android 网络优化(二、网络优化基础篇)上
  14. Latex系列教程 汇总
  15. Python利用Opencv读取图片
  16. 中国原盐产业发展现状分析,原盐主要应用于化工行业「图」
  17. 《JavaEE初阶》HTTP协议和HTTPS
  18. 6、网络设计时原来还要遵循这些原则。
  19. android tun0 流量统计,Android应用流量统计——NetworkStatsManager使用-Go语言中文社区...
  20. Unity编辑器拓展之二十四:基于Unity Node Editor、ScriptableObject的配置化新手引导系统

热门文章

  1. python 跨知乎app发私信以及Python专栏30万用户信息爬取
  2. VMware - 导入/导出OVF
  3. 前端JS获取图片文件的真实格式
  4. 怎样学习AI-Adobe
  5. 企业微信组织架构同步教程
  6. 查看Win7电脑密钥期限
  7. 玩客云:更好的迅雷从“共享计算”开始
  8. 【夏令营保研经验】北理计算机,北航计算机夏令营,中科院霸面保研经验(2019.7)
  9. 史上最全,几百本常用书籍等你来取(面试,java,c,大数据,AI,python,数据结构等)
  10. Qt QPlainTextEdit用法详解