参考资料:《使用Python调用Gurobi求解PDPTW问题(Li & Lim’s benchmark》
下一篇:《【PDPTW】python调用guribo求解PDPTW问题(Li & Lim‘s benchmark)之二》
1 .构造图片中的数据
t2=pd.DataFrame([[10,100,1,2, 2,2,2,2,2],
[0, 0, 0, 0,0,1000, 0,0,0],
[1, 0,10,10,0,1000,10,0,2],
[2, 0,20,-10,0,1000,10,1,0],
[3, 0,30,20,0,1000,20,0,4],
[4,0,40,-20,0,1000,20,3,0],
[5,10,40,30,0,1000,30,0,6],
[6,10,30,-30,0,1000,30,5,0],
[7,10,20,40,0,1000,40,0,8],
[8,10,10,40,0,1000,50,7,0],
[9,0, 0, 0, 0,1000,0, 0, 0]],dtype="int64")
t2.to_csv("smallcase.txt",index=0,sep="\t",header=0)
代开“smallcase.txt"文件,手动删除第一行所有的2,使得10,100,1后面是回车符号
对比一下,构造的数据与原数据loc101.txt的格式相同.注意我这里设置的数据类型也是整型而不是浮点型.
2. 数据含义
第一行:代表车辆的信息
10 100 1表示,车辆的个数为10,其容量为100,车速为1.
25 200 1表示,车辆的个数为25,其容量为200,车速为1.
除首行外:
‘TaskNo’ | ,‘X’, | ‘Y’ | ‘Demand’ | ‘ET’ | ‘LT’ | ‘ST’ | ‘PI’ | ‘DI’ |
---|---|---|---|---|---|---|---|---|
1 | 0 | 10 | 10.0 | 0.0 | 1000.0 | 10.0 | 0.0 | 2.0 |
2 | 0 | 20 | -10.0 | 0.0 | 1000.0 | 10.0 | 1.0 | 0.0 |
为了防止误会,这里的“TaskNo"在这里说成node的ID.
lc101.txt
中8行和10行就可以这样对应起来.需求任务为0的节点设置为Depot
,一个为出发depot,另一个为返回depot.ef read_txt_data(DataPath):
# 读取车辆信息
VehiclesInfo=pd.read_table(DataPath,nrows=1,names=['K','C','S'])
Vehicles={}
for i in range(VehiclesInfo.iloc[0,0]):
Vehicles[i]=[VehiclesInfo.iloc[0,1],VehiclesInfo.iloc[0,2]] # 键i=车辆的序号,值为[车辆容量、速度]
#print(Vehicles)
# 读取Depot和任务信息
ColumnNames=['TaskNo','X','Y','Demand','ET','LT','ST','PI','DI']
# [任务标号,x坐标,y坐标,需求任务]
TaskData=pd.read_table(DataPath,skiprows=[0],names=ColumnNames,index_col='TaskNo')
# 提取Depot和取送货点(Customer)的位置坐标 Locations
nrows=TaskData.shape[0]
Locations={}
for i in range(nrows):
if i not in Locations.keys():
Locations[i]=[TaskData.iloc[i,0],TaskData.iloc[i,1]] # 键为depot或客户编号,值为相应的坐标(x,y)
# 提取Depot和取送货点的Demand
Demand={}
for i in range(nrows):
if i not in Demand.keys():
Demand[i]=TaskData.iloc[i,2]
#print('Demand',Demand)
# 提取Depot和取送货点的最早和最晚取送货时间及时间窗
TimeWindow={}
EarliestTime=TaskData.sort_values(by='ET').iloc[0,3]
LatestTime=TaskData.sort_values(by='LT',ascending=False).iloc[0,4]
for i in range(nrows):
if i not in TimeWindow.keys():
TimeWindow[i]=[TaskData.iloc[i,3],TaskData.iloc[i,4]]
# 提取Depot和取送货点的服务时间ServiceTime
ServiceTime={}
for i in range(nrows):
if i not in ServiceTime.keys():
ServiceTime[i]=TaskData.iloc[i,5]
# 提取运输Request
# 对于取货任务,PICKUP索引为0,而同一行的DELIVERY索引给出相应送货任务的索引
Request={}
count = 0 # 记录运输需求的数量
for i in range(1,nrows-1):
if TaskData.iloc[i,6] == 0:
Request[count]=[i,TaskData.iloc[i,7]] # 将取送货点组合在一起,键为count,值为[取货点,送货点]
count += 1
return Vehicles,Locations,Demand,TimeWindow,ServiceTime,Request,nrows,EarliestTime,LatestTime
def calculate_euclid_distance(x1, y1, x2, y2):
""" 计算Euclid距离.
具体描述:计算两点(x1, y1), (x2, y2)之间的Euclid距离.
"""
EuclidDistance = math.sqrt((x2-x1)**2 + (y2-y1)**2)
return EuclidDistance
def construct_distance_matrix(Locations):
# 构建距离矩阵
DistanceMatrix = {}
for i in Locations.keys():
for j in Locations.keys():
if i != j:
DistanceMatrix[i, j] = calculate_euclid_distance(Locations[i][0], Locations[i][1],
Locations[j][0], Locations[j][1])
# 尝试获取字典DistanceMatrix中最大的值
key=max(DistanceMatrix, key=DistanceMatrix.get)
LongestDistance=DistanceMatrix[key]
return DistanceMatrix,LongestDistance
def construct_time_matrix(Vehilces,DistanceMatrix):
# Vehilces是一个list性质的
# DistanceMatrix是一个字典
TimeMatrix={}
for k in range(len(Vehilces)):
for i in Locations.keys():
for j in Locations.keys():
if i != j:
TimeMatrix[i, j, k] = DistanceMatrix[i, j] / Vehilces[k][1]
return TimeMatrix
目标函数(11)使总路由成本最小化。
约束(12)规定每个顶点必须被精确地服务一次。
等式(13)和(14)保证每辆车从车辆段出发,并在其路线结束时返回车辆段。请注意,这并不意味着必须使用每辆车。车辆只能使用弧线 ( 0 , n + n ~ + 1 ) (0,n+\tilde{n}+1) (0,n+n~+1),即不离开车辆段。
(15)确保了流量守恒。
时间变量用于消除(16)中的子时间,假定 ( t i j k + d i ) > 0 , ∀ ( i , j ) ∈ A (t^k_{ij}+d_i)>0,\forall (i,j)\in A (tijk+di)>0,∀(i,j)∈A.
约束条件(17)和(18)保证车辆在整个行程中不超过其容量。应注意,该公式需要引入额外的决策变量 Q i k Q^k_i Qik,对应于车辆k在顶点i处的总负载。这些变量不需要用于公式化基本VRP,但对于将其扩展到提货和交货问题是必不可少的。在VRP公式中,有时使用类似于(17)的不等式来确保路线连通性(由上述公式中的(16)完成)。此外,在(17)中,仅PDVRP(PDTSP)需要相等。对于所有其他VRPPD类型,可以用“≥”代替。
(16)和(17)中给出的非线性约束可以使用大M公式进行线性化(参见Cordeau 2006)。
(23)(24)时间窗口和最大路由持续时间限制
- Q i k ≤ m a x ( C k , C k + q j ) Q_i^k \leq max(C^k,C^k+q_j) Qik≤max(Ck,Ck+qj):离开i点时的负载量 Q i k Q_i^k Qik,在 q j < 0 q_j<0 qj<0时即要装载的时候,不能超过 C k − ∣ q j ∣ C^k-\vert q_j\vert Ck−∣qj∣. 所以大M法为: Q i k ≤ Q j k + q j + M ( 1 − x i j k ) Q_i^k\leq Q_j^k+q_j+M(1-x^k_{ij}) Qik≤Qjk+qj+M(1−xijk)
- 同理,换为: Q i k ≥ Q j k − q j − M ( 1 − x i j k ) Q_i^k \geq Q_j^k-q_j -M(1-x_{ij}^k) Qik≥Qjk−qj−M(1−xijk)
一般来说,求解一个数学规划模型的时候,通常会按照如下步骤解决问题:
设置变量—addVar()。
更新变量空间—update()。
设定目标函数—setObjective()。
设定约束条件—addConstr()。
执行最优化—optimize()。
Gurobi model object. Commonly used methods on this object are:
getConstrs(): Get a list of constraints in the model
getJSONSolution(): Get a JSON-string representation of the current solution(s) to the model
getParamInfo(paramname): Get information on a model parameter.
getVars(): Get a list of variables in the model
optimize(): Optimize the model.
printAttr(attrname, filter): Print attribute values.
printQuality(): Print solution quality statistics.
printStats(): Print model statistics.
read(filename): Read model data (MIP start, basis, etc.) from a file
reset(): Discard any resident solution information.
resetParams(): Reset all parameters to their default values.
setParam(paramname, newval): Set a model parameter to a new value.
write(filename): Write model data to a file.
Additional methods on this object are:
addConstr(), addGenConstrMax(), addGenConstrMin(), addGenConstrAbs(),
addGenConstrAnd(), addGenConstrOr(), addGenConstrNorm(),
addGenConstrIndicator(), aaddGenConstrPWL(), addGenConstrPoly(),
addGenConstrExp(), addGenConstrExpA(), addGenConstrLog(),
addGenConstrLogA(), addGenConstrPow(), addGenConstrSin(),
addGenConstrCos(), addGenConstrTan(), addGenConstrLogistic(),
addRange(), addSOS(), addVar(), chgCoeff(), computeIIS(), copy(), fixed(),
getCoeff(), getCol(), getRow(), message(), presolve(), relax(), terminate(),
update()
参考资料:《Gurobi使用(一)——操作指南》
addVar()
x = model.addVar(lb=0.0, ub=gurobipy.GRB.INFINITY, vtype=gurobipy.GRB.CONTINUOUS, name="")
lb=0.0:变量的下界,默认为0.0。
ub=gurobipy.GRB.INFINITY:变量的上界,默认为无穷大。
vtype=gurobipy.GRB.CONTINUOUS:变量的类型,默认为连续型号。变为GRB.BINARY则是0-1变量,变为GRB.INTEGER则为整数变量。
name=“”:变量名,默认为空。
# 创建模型
model = Model()#gurobipy中的Model()
# 变量下标存放字典
xindex = {} # 存储变量xijk的下标ijk,表示车辆k是否经过弧ij
qindex = {} # 存储变量qik的下标ik,表示车辆k即将离开i时的载货量
bindex = {} # 存储变量bik的下标ik,表示车辆k开始服务i的时间
for k in Vehicles.keys():
for i in range(nrows):
qindex[i,k] = 0
bindex[i,k] = 0
for j in range(nrows):
if i != j:
xindex[i,j,k] = 0
x = model.addVars(xindex.keys(), vtype=GRB.BINARY, name='x') # 变量x_{ijk},GRB.BINARY表示{0,1}变量
q = model.addVars(qindex.keys(), vtype=GRB.INTEGER, name='q') # 变量q_{ik},GRB.INTEGER为整数变量
b = model.addVars(bindex.keys(), vtype=GRB.CONTINUOUS, name='b') # 变量b_{ik},GRB.CONTINUOUS为连续变量
约束(1)
除了depot外,每个节点只被服务一次.这里depot的id=0.P是连线的起点,D时连线的终点.
∀ i ∈ P ∪ D = { 1 , 2 , ⋯ , n , n + 1 , ⋯ , n + n } \forall i \in P\cup D=\{1,2,\cdots,n,n+1,\cdots,n+n\} ∀i∈P∪D={1,2,⋯,n,n+1,⋯,n+n}
∑ k ∈ K ∑ j : ( i , j ) ∈ A x i j k = 1 \sum_{k\in K}\sum_{j:(i,j)\in A}x^k_{ij}=1 k∈K∑j:(i,j)∈A∑xijk=1
for i in range(1, nrows-1):
model.addConstr(x.sum(i,'*','*') == 1) # 星号*的用法
约束(2)和(3)
每辆车必须从depot出发,最后回到depot.其中起点的个数为n,终点的个数为 n ~ \tilde{n} n~
∀ k ∈ K \forall k \in K ∀k∈K
∑ j : ( 0 , j ) ∈ A x 0 , j k = 1 \sum_{j:(0,j)\in A}x_{0,j}^k=1 j:(0,j)∈A∑x0,jk=1
∑ i : ( i , n + n ~ + 1 ) ∈ A x i , n + n ~ + 1 k = 1 \sum_{i:(i,n+\tilde{n}+1)\in A} x^k_{i, n+\tilde{n}+1}=1 i:(i,n+n~+1)∈A∑xi,n+n~+1k=1
for k in Vehicles.keys():
model.addConstr(x.sum(0,'*',k) == 1) # 车辆k从depot出发
model.addConstr(x.sum('*',nrows-1,k) == 1) # 车辆k回到depot
约束(4)
Flow conservation 流平衡约束.车辆到达节点i的次数==车辆从节点i离开的次数(要么0要么1)
∀ k ∈ K \forall k \in K ∀k∈K
∀ j ∈ P ∪ D \forall j \in P\cup D ∀j∈P∪D
∑ j : ( i , j ) ∈ A x i j k − ∑ i : ( j , i ) ∈ A x j i k = 0 \sum_{j:(i,j)\in A}x^k_{ij}-\sum_{i:(j,i)\in A}x_{ji}^k=0 j:(i,j)∈A∑xijk−i:(j,i)∈A∑xjik=0
for k in Vehicles.keys():
for i in range(1, nrows-1):
# 车辆k在P和D的流平衡约束
model.addConstr(x.sum(i,'*',k) == x.sum('*',i,k))
约束(5)
用时间变量来消除子回路约束. xindex[i,j,k]
∀ k ∈ K \forall k \in K ∀k∈K, ∀ ( i , j ) ∈ A \forall (i,j)\in A ∀(i,j)∈A
x i j k = 1 → B j k ≥ B i k + d i + t i j k x_{ij}^k=1 \to B_j^k \geq B_i^k+d_i+t_{ij}^k xijk=1→Bjk≥Bik+di+tijk
大M线性化为
B j k + M ( 1 − x i j k ) ≥ B i k + d i + t i j k B_j^k+M(1-x_{ij}^k) \geq B_i^k+d_i+t_{ij}^k Bjk+M(1−xijk)≥Bik+di+tijk
# 需要用大M法来线性化该约束,M定义为 2*(LatestTime+LongestDistance)
for i in xindex.keys():
model.addConstr(b[i[1],i[2]] + 2 * (1 - x[i]) * (LatestTime+LongestDistance) >=
b[i[0],i[2]] + ServiceTime[i[0]] + TimeMatrix[i])
约束(6)
载货量平衡约束,需要用大M法来线性化该约束
∀ k ∈ K \forall k \in K ∀k∈K, ∀ ( i , j ) ∈ A \forall (i,j)\in A ∀(i,j)∈A
x i j k = 1 → Q j k = Q i k + q j x_{ij}^k=1 \to Q_j^k=Q_i^k+q_j xijk=1→Qjk=Qik+qj
大M线性化为:
Q j k + M ( 1 − x i j k ) ≥ Q i k + q j Q_j^k+M(1-x_{ij}^k) \geq Q_i^k +q_j Qjk+M(1−xijk)≥Qik+qj
# M定义为 100*车辆最大载量
for i in xindex.keys():
model.addConstr(q[i[1],i[2]] + (1- x[i]) * (100*Vehicles[i[2]][0])>=
q[i[0],i[2]]+Demand[i[1]])
约束(7)
车辆载量约束
∀ i ∈ V , k ∈ K \forall i \in V, k\in K ∀i∈V,k∈K
m a x { 0 , q i } ≤ Q i k ≤ m i n { C k , C k + q i k } max\{0,q_i\} \leq Q_i^k \leq min\{C^k,C^k+q_i^k\} max{0,qi}≤Qik≤min{Ck,Ck+qik}
转化为4个单向的条件约束
Q i k ≥ 0 Q_i^k \geq 0 Qik≥0
Q i k ≥ q i Q_i^k \geq q_i Qik≥qi
Q i k ≤ C k Q_i^k \leq C^k Qik≤Ck
Q i k ≤ C k + q i k Q_i^k \leq C^k+q_i^k Qik≤Ck+qik
for i in qindex.keys():
model.addConstr(q[i]>=0)
model.addConstr(q[i]>=Demand[i[0]])
model.addConstr(q[i]<=Vehicles[i[1]][0])
model.addConstr(q[i]<=Vehicles[i[1]][0]+Demand[i[0]])
约束(8)
添加depot的时间窗口约束,添加节点的时间窗口约束.这里的V包含depot和节点.depot的id为0.
∀ i ∈ V , ∀ k ∈ K \forall i \in V, \forall k \in K ∀i∈V,∀k∈K,
e i ≤ B i q ≤ l i e_i \leq B_i^q \leq l_i ei≤Biq≤li
for k in range(len(Vehicles)):
# 两个depot的时间窗约束
model.addConstr(b[0,k] >= TimeWindow[0][0])
model.addConstr(b[0,k] <= TimeWindow[0][1])
model.addConstr(b[nrows-1,k] >= TimeWindow[nrows-1][0])
model.addConstr(b[nrows-1,k] <= TimeWindow[nrows-1][1])
Request:{0: [1, 2], 1: [3, 4], 2: [5, 6], 3: [7, 8]}
在这里时设置了已经明确由 1 → 2 1 \to 2 1→2, 3 → 4 3\to 4 3→4这种线路的的节点的要满足时间窗口的要求.
for k in range(len(Vehicles)):
for r in range(len(Request)):
# 运输请求i两个节点的左时间窗
#Request:{0: [1, 2], 1: [3, 4], 2: [5, 6], 3: [7, 8]}
#TW{0: [0, 1000], 1: [0, 1000], 2: [0, 1000], 3: [0, 1000],..}
model.addConstr(b[Request[r][0],k]>=TimeWindow[Request[r][0]] [0])
model.addConstr(b[Request[r][1],k]>=TimeWindow[Request[r][1]][0])
# 运输请求i两个节点的右时间窗
model.addConstr(b[Request[r][0],k]<=TimeWindow[Request[r][0]][1])
model.addConstr(b[Request[r][1],k]<=TimeWindow[Request[r][1]][1])
为什么不对所有的节点设置时间窗口 呢?,这里我也没搞明白.为了方便理解,我将原市循环的 ( i , j ) → ( k , r ) (i,j) \to (k,r) (i,j)→(k,r)
# 按三引号内部的方式写,会导致错误KeyError: (0, 0)
for i in bindex.keys():
model.addConstr(b[i] >= TimeWindow[i][0])
model.addConstr(b[i] <= TimeWindow[i][1])
如果当前路径中要求存在一些特殊固定的路线.如:Request的时候,则做一些特殊的约束.
约束(9)
弧(i,j)必须由同一辆车经过(先取货再送货).假设这些弧集为特定的集合 A ~ \tilde{A} A~
∀ k ∈ K \forall k \in K ∀k∈K, ∀ ( i , j ) ∈ A ~ \forall (i,j)\in \tilde{A} ∀(i,j)∈A~
∑ j : ( i , j ) ∈ A ~ x i j k = ∑ i : ( i , j ) ∈ A ~ x i j k \sum_{j:(i,j)\in \tilde{A}}x_{ij}^k=\sum_{i:(i,j)\in \tilde{A}}x_{ij}^k j:(i,j)∈A~∑xijk=i:(i,j)∈A~∑xijk
for r in range(len(Request)):
for k in range(len(Vehicles)):
model.addConstr(x.sum(Request[r][0],'*',k)==x.sum('*',Request[r][1],k))
约束(10)
弧(i,j)必须i在前,j在后.有顺序性
∀ k ∈ K \forall k \in K ∀k∈K, ∀ ( i , j ) ∈ A ~ \forall (i,j)\in \tilde{A} ∀(i,j)∈A~
B i k ≤ B j k B^k_i \leq B_j^k Bik≤Bjk
for r in range(len(Request)):
for k in range(len(Vehicles)):
model.addConstr(b[Request[r][0],k]<=b[Request[r][1],k])
# 目标函数(3):最小化车辆总的行驶距离
DistanceCost = 0
for k in range(len(Vehicles)):
for i in range(len(Locations)):
for j in range(len(Locations)):
if i != j:
DistanceCost += (DistanceMatrix[i,j] * x[i,j,k])
model.setObjective(DistanceCost, GRB.MINIMIZE)
model.update()
model.__data = x,b,q
start = time.time()
DataPath="smallcase.txt"
Vehicles,Locations,Demand,TimeWindow,ServiceTime,Request,nrows,EarliestTime,LatestTime = read_txt_data(DataPath)
# 构建距离矩阵
DistanceMatrix,LongestDistance = construct_distance_matrix(Locations)
# 构建通行时间矩阵
TimeMatrix = construct_time_matrix(Vehicles,DistanceMatrix)
# 建立与优化模型
model,xindex = build_pdptw_model(Vehicles,Locations,Demand,TimeWindow,ServiceTime,Request,nrows,EarliestTime,LatestTime,DistanceMatrix,LongestDistance,TimeMatrix)
model.params.TimeLimit = 1000 # 设定模型求解时间
model.write('PDPTW_%s.lp' % DataPath)
model.write('PDPTW_%s.mps' % DataPath)
model.setParam(GRB.Param.LogFile, 'PDPTW_%s.log' % DataPath)
model.optimize()
x,b,q = model.__data
# 输出方式2
for k in range(len(Vehicles)):
for i in range(nrows):
for j in range(nrows):
if i != j:
if (x[i,j,k].x - 0.5 > 0):
print('tour for vehicle %s:' % k)
print('%s-->%s' % (i,j))
print("Optimal solution:",model.ObjVal)
# 计算程序运行时间
end = time.time()
print('程序运行时间:')
print(end-start)
结果:
车辆 {0: [100, 1], 1: [100, 1], 2: [100, 1], 3: [100, 1], 4: [100, 1], 5: [100, 1], 6: [100, 1], 7: [100, 1], 8: [100, 1], 9: [100, 1]}
Demand {0: 0, 1: 10, 2: -10, 3: 20, 4: -20, 5: 30, 6: -30, 7: 40, 8: 40, 9: 0}
时间窗口 {0: [0, 1000], 1: [0, 1000], 2: [0, 1000], 3: [0, 1000], 4: [0, 1000], 5: [0, 1000], 6: [0, 1000], 7: [0, 1000], 8: [0, 1000], 9: [0, 1000]}
任务 {0: [1, 2], 1: [3, 4], 2: [5, 6], 3: [7, 8]}
Set parameter Username
Academic license - for non-commercial use only - expires 2023-12-26
Set parameter TimeLimit to value 1000
Set parameter LogFile to value "PDPTW_smallcase.txt.log"
Gurobi Optimizer version 10.0.0 build v10.0.0rc2 (win64)
CPU model: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz, instruction set [SSE2|AVX|AVX2]
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
Optimize a model with 2588 rows, 1100 columns and 9060 nonzeros
Model fingerprint: 0xd148a0cd
Variable types: 100 continuous, 1000 integer (900 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+04]
Objective range [1e+01, 4e+01]
Bounds range [1e+00, 1e+00]
RHS range [1e+00, 1e+04]
Presolve removed 740 rows and 50 columns
Presolve time: 0.03s
Presolved: 1848 rows, 1050 columns, 11628 nonzeros
Variable types: 100 continuous, 950 integer (850 binary)
Root relaxation: objective 8.000000e+01, 60 iterations, 0.00 seconds (0.00 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 80.00000 0 11 - 80.00000 - - 0s
0 0 80.00000 0 21 - 80.00000 - - 0s
H 0 0 94.1421356 80.00000 15.0% - 0s
0 0 80.00000 0 25 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 11 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 16 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 14 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 19 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 16 94.14214 80.00000 15.0% - 0s
0 0 80.00000 0 11 94.14214 80.00000 15.0% - 0s
0 0 80.62902 0 16 94.14214 80.62902 14.4% - 0s
0 0 80.69077 0 16 94.14214 80.69077 14.3% - 0s
0 2 80.84309 0 16 94.14214 80.84309 14.1% - 0s
Cutting planes:
Learned: 1
Gomory: 1
Implied bound: 39
MIR: 13
StrongCG: 3
RLT: 5
Relax-and-lift: 3
Explored 68 nodes (2136 simplex iterations) in 0.57 seconds (0.36 work units)
Thread count was 8 (of 8 available processors)
输出结果:
Best objective 9.414213562373e+01, best bound 9.414213562373e+01, gap 0.0000%
tour for vehicle 0:
0-->9
tour for vehicle 1:
0-->9
tour for vehicle 2:
0-->9
tour for vehicle 3:
0-->9
tour for vehicle 4:
0-->9
tour for vehicle 5:
0-->1
tour for vehicle 5:
1-->2
tour for vehicle 5:
2-->3
tour for vehicle 5:
3-->4
tour for vehicle 5:
4-->5
tour for vehicle 5:
5-->6
tour for vehicle 5:
6-->7
tour for vehicle 5:
7-->8
tour for vehicle 5:
8-->9
tour for vehicle 6:
0-->9
tour for vehicle 7:
0-->9
tour for vehicle 8:
0-->9
tour for vehicle 9:
0-->9
Optimal solution: 94.14213562373095
程序运行时间:
0.7380778789520264
Process finished with exit code 0
下一篇:《【PDPTW】python调用guribo求解PDPTW问题(Li & Lim‘s benchmark)之二》