手把手教你用Gurobi求解一个数学模型

  • 手把手教你用Gurobi求解一个数学模型
    • 带时间窗的车辆路径规划问题(Vrptw)
    • python调用Gurobi求解Vrptw
      • 首先我们定义一下需要用到的参数:
      • 定义一个读取数据的函数,并对节点之间的距离进行计算:
      • 读取数据,并定义一些参数:
      • 调用gurobi进行模型的建立与求解:
        • 根据式(9)定义决策变量,并加入模型当中:
        • 根据式(1)定义目标函数,并加入模型当中:
        • 根据式(2)~(8)定义决策变量,并加入模型当中:

手把手教你用Gurobi求解一个数学模型

在接触Gurobi之前,一直使用Java语言调用cplex求解数学模型,这段时间在师兄的指点下,学习了使用python调用Gurobi的一些基础操作,感叹实在是太简易了。
在此分享一个求解Vrptw问题的小例子。

带时间窗的车辆路径规划问题(Vrptw)

对于Vrptw问题来说,数学模型主要由以下部分组成。首先我们定义一些相关参数,一个图可以表示为G(V,A)G(V,A)G(V,A),其中V={0,1,...,n,n+1}V= \{ 0,1,...,n,n+1 \}V={0,1,...,n,n+1}为图中所有点的集合,AAA为图中所有弧的集合,有(i,j)∈(i,j)\in(i,j)∈A,∀i,j∈V,i≠j\forall i,j\in V, i\ne j∀i,j∈V,i​=j。弧(i,j)(i,j)(i,j)的单位运输费用为cijc_{ij}cij​,运输时间为tijt_{ij}tij​,每个客户点的需求为qijq_{ij}qij​,可服务的时间窗为[ei,li][e_i,l_i][ei​,li​],服务时长为serviserv_iservi​。令车辆的集合为KKK,每辆车的最大载重为Qk,∀k∈KQ_k,\forall k\in KQk​,∀k∈K。决策变量为xijkx_{ij}^kxijk​,代表第kkk辆车是否服务了弧(i,j)(i,j)(i,j);sis_isi​为客户点iii开始被服务的时间。

接下来构建数学模型。
目标函数为最小化运输成本:
min∑k∈K∑(i,j)∈Acijxij(1)min \sum_{k\in K} \sum_{(i,j)\in A}c_{ij}x_{ij} \tag{1} mink∈K∑​(i,j)∈A∑​cij​xij​(1)
约束一让车辆驶出仓库(depot):
∑(0,j)∈Ax0jk=1,∀k∈K(2)\sum_{(0,j)\in A}x_{0j}^k =1,\ \forall k\in K \tag{2} (0,j)∈A∑​x0jk​=1, ∀k∈K(2)
约束二为流平衡(除去depot之外的点):
∑(i,j)∈Axijk−∑(j,i)∈Axjik=0,∀k∈K,i∈V∖{s,t}(3)\sum_{(i,j)\in A}x_{ij}^k - \sum_{(j,i)\in A}x_{ji}^k = 0,\ \forall k\in K ,i \in V\setminus \{s,t\} \tag{3} (i,j)∈A∑​xijk​−(j,i)∈A∑​xjik​=0, ∀k∈K,i∈V∖{s,t}(3)
约束三让车辆驶回depot:
∑(i,n+1)∈Axi,n+1k=1,∀k∈K(4)\sum_{(i,n+1)\in A}x_{i,n+1}^k =1,\ \forall k\in K \tag{4} (i,n+1)∈A∑​xi,n+1k​=1, ∀k∈K(4)
约束四保证每个客户点都被服务:
∑k∈K∑(i,j)∈Axi,jk=1,∀i∈V∖{s,t}(5)\sum_{k \in K}\sum_{(i,j)\in A}x_{i,j}^k =1,\ \forall i \in V\setminus \{s,t\} \tag{5} k∈K∑​(i,j)∈A∑​xi,jk​=1, ∀i∈V∖{s,t}(5)
约束五保证被服务的相邻节点开始服务时间的大小关系(去回路):
si+tij+servi−M(1−xij)≤sj,∀(i,j)∈A(6)s_i+t_{ij}+serv_i-M(1-x_{ij})\le s_j,\ \forall (i,j) \in A \tag{6} si​+tij​+servi​−M(1−xij​)≤sj​, ∀(i,j)∈A(6)
约束六保证不违反客户的时间窗:
ei≤si≤li,∀i∈V∖{s,t}(7)e_i \le s_i \le l_i ,\ \forall i \in V\setminus \{s,t\} \tag{7} ei​≤si​≤li​, ∀i∈V∖{s,t}(7)
约束七保证不违反车辆的载重约束:
∑(i,j)∈Axijkqi≤Qk,∀k∈K(8)\sum_{(i,j)\in A}x_{ij}^kq_i \le Q_k ,\ \forall k \in K \tag{8} (i,j)∈A∑​xijk​qi​≤Qk​, ∀k∈K(8)
最后是决策变量的取值约束:
xijk∈{0,1}∀(i,j)∈A,k∈Ksi≥0,∀i∈V∖{s,t}(9)x_{ij}^k\in \{0,1\} \ \forall (i,j) \in A,k \in K \\ s_i \ge 0,\ \forall i \in V\setminus \{s,t\} \tag{9} xijk​∈{0,1} ∀(i,j)∈A,k∈Ksi​≥0, ∀i∈V∖{s,t}(9)
那么(1)~(9)式就组成了Vrptw问题的数学模型。

python调用Gurobi求解Vrptw

首先我们定义一下需要用到的参数:

class Data:customerNum = 0nodeNum     = 0vehicleNum  = 0capacity    = 0cor_X       = []cor_Y       = []demand      = []serviceTime = []readyTime   = []dueTime     = []disMatrix   = [[]]

定义一个读取数据的函数,并对节点之间的距离进行计算:

# function to read data from .txt files
def readData(data, path, customerNum):data.customerNum = customerNumdata.nodeNum = customerNum + 2f = open(path, 'r')lines = f.readlines()count = 0# read the infofor line in lines:count = count + 1if (count == 5):line = line[:-1].strip()str = re.split(r" +", line)data.vehicleNum = int(str[0])data.capacity = float(str[1])elif (count >= 10 and count <= 10 + customerNum):line = line[:-1]str = re.split(r" +", line)data.cor_X.append(float(str[2]))data.cor_Y.append(float(str[3]))data.demand.append(float(str[4]))data.readyTime.append(float(str[5]))data.dueTime.append(float(str[6]))data.serviceTime.append(float(str[7]))data.cor_X.append(data.cor_X[0])data.cor_Y.append(data.cor_Y[0])data.demand.append(data.demand[0])data.readyTime.append(data.readyTime[0])data.dueTime.append(data.dueTime[0])data.serviceTime.append(data.serviceTime[0])# compute the distance matrixdata.disMatrix = [([0] * data.nodeNum) for p in range(data.nodeNum)]  # 初始化距离矩阵的维度,防止浅拷贝# data.disMatrix = [[0] * nodeNum] * nodeNum]; 这个是浅拷贝,容易重复for i in range(0, data.nodeNum):for j in range(0, data.nodeNum):temp = (data.cor_X[i] - data.cor_X[j]) ** 2 + (data.cor_Y[i] - data.cor_Y[j]) ** 2data.disMatrix[i][j] = math.sqrt(temp)temp = 0return data

读取数据,并定义一些参数:

data = Data()
path = 'c101.txt' //读取Solomon数据集
customerNum = 20  //设置客户数量
readData(data, path, customerNum)
data.vehicleNum = 10  //设置车辆数
printData(data, customerNum)
BigM = 100000 //定义一个极大值

调用gurobi进行模型的建立与求解:

x = {}
s = {}  //定义字典用来存放决策变量

根据式(9)定义决策变量,并加入模型当中:

for i in range(data.nodeNum):for k in range(data.vehicleNum):name = 's_' + str(i) + '_' + str(k)s[i,k] = model.addVar(0, 1500, vtype= GRB.CONTINUOUS, name= name)  //定义访问时间为连续变量for j in range(data.nodeNum):if(i != j):name = 'x_' + str(i) + '_' + str(j) + '_' + str(k)x[i,j,k] = model.addVar(0, 1, vtype= GRB.BINARY, name= name)  //定义是否服务为0-1变量

根据式(1)定义目标函数,并加入模型当中:

//首先定义一个线性表达式
obj = LinExpr(0)
for i in range(data.nodeNum):for k in range(data.vehicleNum):for j in range(data.nodeNum):if(i != j)://将目标函数系数与决策变量相乘,并进行连加obj.addTerms(data.disMatrix[i][j], x[i,j,k])
//将表示目标函数的线性表达式加入模型,并定义为求解最小化问题
model.setObjective(obj, GRB.MINIMIZE)

根据式(2)~(8)定义决策变量,并加入模型当中:

约束一(vehicle_depart):

for k in range(data.vehicleNum)://同样先定义一个线性表达式lhs = LinExpr(0) for j in range(data.nodeNum):if(j != 0)://约束系数与决策变量相乘   lhs.addTerms(1, x[0,j,k])  //将约束加入模型model.addConstr(lhs == 1, name= 'vehicle_depart_' + str(k))

约束二(flow_conservation):

for k in range(data.vehicleNum):for h in range(1, data.nodeNum - 1):expr1 = LinExpr(0)expr2 = LinExpr(0)for i in range(data.nodeNum):if (h != i):expr1.addTerms(1, x[i,h,k])for j in range(data.nodeNum):if (h != j):expr2.addTerms(1, x[h,j,k])model.addConstr(expr1 == expr2, name= 'flow_conservation_' + str(i))expr1.clear()expr2.clear()

约束三(vehicle_enter):

for k in range(data.vehicleNum):lhs = LinExpr(0)for j in range(data.nodeNum - 1):if(j != 0):lhs.addTerms(1, x[j, data.nodeNum-1, k])model.addConstr(lhs == 1, name= 'vehicle_enter_' + str(k))

约束四(customer_visit):

for i in range(1, data.nodeNum - 1):lhs = LinExpr(0) for k in range(data.vehicleNum):for j in range(1, data.nodeNum):if(i != j):lhs.addTerms(1, x[i,j,k])  model.addConstr(lhs == 1, name= 'customer_visit_' + str(i))

约束五(time_windows):

for k in range(data.vehicleNum):for i in range(data.nodeNum):for j in range(data.nodeNum):if(i != j):model.addConstr(s[i,k] + data.disMatrix[i][j] + data.serviceTime[i] - s[j,k]- BigM + BigM * x[i,j,k] <= 0 , name= 'time_windows_')

约束六(ready_time and due_time):

for i in range(1,data.nodeNum-1):for k in range(data.vehicleNum):model.addConstr(data.readyTime[i] <= s[i,k], name= 'ready_time')model.addConstr(s[i,k] <= data.dueTime[i], name= 'due_time')

约束七(capacity_vehicle):

for k in range(data.vehicleNum):lhs = LinExpr(0)for i in range(1, data.nodeNum - 1):for j in range(data.nodeNum):if(i != j):lhs.addTerms(data.demand[i], x[i,j,k])model.addConstr(lhs <= data.capacity, name= 'capacity_vehicle' + str(k))

求解模型(solve)并输出解:

model.optimize()
print("\n\n-----optimal value-----")
print(model.ObjVal)for key in x.keys():if(x[key].x > 0 ):print(x[key].VarName + ' = ', x[key].x)

导出模型:

model.write('VRPTW.lp')

求解结果(部分):

这样就完成了,感谢大家的阅读。

作者:夏旸,清华大学,工业工程系/深圳国际研究生院 (硕士在读)
刘兴禄,清华大学,清华伯克利深圳学院(博士在读)

完整Project文件请关注运小筹公众号,回复【Vrptw】获取。

手把手教你用Gurobi求解一个数学模型相关推荐

  1. python界面设计-手把手教你用Python设计一个简单的命令行界面

    原标题:手把手教你用Python设计一个简单的命令行界面 对 Python 程序来说,完备的命令行界面可以提升团队的工作效率,减少调用时可能碰到的困扰.今天,我们就来教大家如何设计功能完整的 Pyth ...

  2. 手把手教你用C#写一个刷屏软件

    手把手教你用C#写一个刷屏轰炸软件 成品展示 环境准备 新建项目 程序思路 程序部分 完整代码 成品展示 环境准备 VS2019 新建项目 打开界面绘制 打开工具箱开始放置按钮标签以及文本框 最后设计 ...

  3. IP门禁:手把手教你用PHP实现一个IP防火墙

    最近我遇到一个需求,我的一台服务器总是遭到端口扫描和恶意登录攻击,对此可以怎么办呢?似乎除了内网隔离.增强密码认证.证书登录.设置防火墙iptables,网上找不到什么别的方案,对了,还用堡垒机的方案 ...

  4. 超详细——手把手教你用threejs实现一个酷炫的模型发光扫描效果(三)

    上一篇文章 voidjay,公众号:web前端可视化超详细--手把手教你用threejs实现一个酷炫的模型发光扫描效果(二) 上一篇文章已完成基本效果的实现,本文则完成整个项目的灵魂:发光效果以及模型 ...

  5. 手把手教你使用nodejs编写一个【使用远程仓库模板,快速创建项目模块】的cli(命令行)

    目录 实现步骤 初始化cli项目 项目目录 创建交互式命令 拉取远程仓库代码,读取仓库中的模板 拉取远程仓库代码 ora 终端 loading 读取仓库中的模板 将选择的模板复制写入目标项目 Comm ...

  6. 优化| 手把手教你学会杉树求解器(COPT)的安装、配置与测试

    优化| 手把手教你学会杉数求解器COPT的安装.配置与测试 前言 线性规划(LP)测试榜单--单纯形法: Benchmark of Simplex LP solvers 线性规划(LP)测试榜单--内 ...

  7. 如何用python开发游戏_手把手教你用Python完成一个控制台小游戏-阿里云开发者社区...

    很多人想学Python程序设计或者已经了解过一点Python程序设计基础,却没办法开发出一个项目. 今天,通过演示一个简单的控制台小游戏制作,手把手教你如何用Python编写一个游戏程序,即便你是个新 ...

  8. 手把手教你用ESP32 制作一个游戏机,小白可上手

    MAKER: JuanF92/译:趣无尽 相逢已是初识 MicroByte 是一款微型主机,能够运行 NES.GameBoy.GameBoy Color.Game Gear 和 Sega Master ...

  9. 手把手教你用Python打造一个语音合成系统

    击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 大弦嘈嘈如急雨,小弦切切如私语. / ...

  10. 手把手教你用MindSpore训练一个AI模型!

    首先我们要先了解深度学习的概念和AI计算框架的角色(https://zhuanlan.zhihu.com/p/463019160),本篇文章将演示怎么利用MindSpore来训练一个AI模型.和上一章 ...

最新文章

  1. 重定向程序无法决定链接类型 解决方案
  2. 怎样写C代码——《狂人C》习题解答1(第一章习题7)
  3. 走进WebApiClientCore的设计
  4. Pandas知识点-equals()与==的区别
  5. 2021-2025年中国独立式梳妆浴缸行业市场供需与战略研究报告
  6. 函数(八)-函数和匿名函数
  7. GitHub添加SSH-key的步骤
  8. css设置div垂直居中
  9. 苹果修改wifi密码登陆服务器密码,iphone手机修改wifi密码
  10. 大数据开发岗面试30天冲刺 - 日积月累,每日五题【Day01】——Hive1
  11. 小型软件企业组织结构
  12. 实现ppt幻灯片播放倒计时
  13. 环信java_java环信服务端注册IM代码
  14. 快手短视频直播间怎么提高人气热度,直播间冷启动是什么?
  15. 最能拉出同行差距的细节,99%的零售店老板都错过了
  16. 读取excel文件后计算指定行列笛卡儿积并写出
  17. Android 通过okhttp + jsoup 爬虫爬取网页小说
  18. WebInspect评估版试用第2天和第3天
  19. [乐意黎]2016年中级会计师考试《会计实务》试题真题答案-第一批(9.10-9.11)
  20. 工程地质计算机应用百度云,《工程地质计算机应用》2006年第2期电子版

热门文章

  1. win7或win10系统的打印机共享设置步骤
  2. Android Intent定义选择器打开相机和相册
  3. C++实验02(02)华氏温度转换为摄氏温度
  4. Failed to process import candidates for configuration class :Annotation-specified bean name ‘XXX‘ fo
  5. 360主机卫士linux安装软件,360主机卫士linux版|360主机卫士 for Linux v0.5.7官方版 - 121下载站...
  6. Mapgis技巧篇1
  7. QQ聊天记录恢复、迁移教程(改变默认存储位置、个人文件夹保存位置)【转载】
  8. 用虚数i与欧拉公式来解释分数阶微积分
  9. php编写解一元一次方程,一元一次方程及解法
  10. 基于NanoPi3(三星S5P6818)的u-boot移植(一)