[PDPTW]解读Python调用Gurobi求解PDPTW问题(Li & Lim‘s benchmark)之一

参考资料:《使用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’
101010.00.01000.010.00.02.0
2020-10.00.01000.010.01.00.0

为了防止误会,这里的“TaskNo"在这里说成node的ID.

  • 在节点1.其(x,y)=(0,10),有一个(取货)装载10辆车的需求任务.在节点1的最早服务时间为EarliestTime=ET=0,最晚的服务时间为LatestTime=LT=1000.在节点1的服务时间消耗为ServiceTime=ST=10. 并将取货和送货地点联系起来. Di是送货节点的id.(Pi是取货节点的ID).
  • 在节点2.其(x,y)=(0,20),有一个(送货)卸载10辆车的需求任务.在节点2的最早服务时间为EarliestTime=ET=0,最晚的服务时间为LatestTime=LT=1000.在节点2的服务时间消耗为ServiceTime=ST=10. 并将取货和送货地点联系起来. Di是送货节点的id.(Pi是取货节点的ID).
  • 即这两条可以合并为一个取送货的问题.从节点1取货10运送到节点2卸货10.在lc101.txt中8行和10行就可以这样对应起来.
  • 在第一行和最后一行,其需求任务为0的节点设置为Depot,一个为出发depot,另一个为返回depot.

一、读取数据

  • 车辆信息
  • depot和节点的(x,y)坐标信息、demand信息、服务窗口时间信息、服务时间信息,
  • 任务索引(比如从8送到10)
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

二、计算距离矩阵(位置距离,时间距离)

  1. 定义距离的公式(欧式距离)
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 
  1. 构建距离矩阵
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

  1. 构建时间矩阵
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

三、建立pdptw问题的模型

3.1 公式约束

在这里插入图片描述
在这里插入图片描述
目标函数(11)使总路由成本最小化。
约束(12)规定每个顶点必须被精确地服务一次。
等式(13)和(14)保证每辆车从车辆段出发,并在其路线结束时返回车辆段。请注意,这并不意味着必须使用每辆车。车辆只能使用弧线 ( 0 , n + n ~ + 1 ) (0,n+\tilde{n}+1) 0n+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)时间窗口和最大路由持续时间限制

在这里插入图片描述
大M转换法:

  • Q i k ≤ m a x ( C k , C k + q j ) Q_i^k \leq max(C^k,C^k+q_j) Qikmax(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 Ckqj. 所以大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}) QikQjk+qj+M(1xijk)
  • 同理,换为: 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) QikQjkqjM(1xijk)

3.2 学习gurobipy中的Model()

一般来说,求解一个数学规划模型的时候,通常会按照如下步骤解决问题:
设置变量—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=“”:变量名,默认为空。

3.3 建立PDPTW的Model()

1. 添加变量: x i j k , B i k , Q i k x_{ij}^k, B_i^k,Q_i^k xijk,Bik,Qik
# 创建模型
 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为连续变量
2. 添加约束条件

约束(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\} iPD={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 kKj:(i,j)Axijk=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 kK
∑ j : ( 0 , j ) ∈ A x 0 , j k = 1 \sum_{j:(0,j)\in A}x_{0,j}^k=1 j:(0,j)Ax0,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)Axi,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 kK
∀ j ∈ P ∪ D \forall j \in P\cup D jPD
∑ 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)Axijki:(j,i)Axjik=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 kK, ∀ ( 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=1BjkBik+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(1xijk)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 kK, ∀ ( 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=1Qjk=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(1xijk)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 iV,kK
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}Qikmin{Ck,Ck+qik}
转化为4个单向的条件约束
Q i k ≥ 0 Q_i^k \geq 0 Qik0
Q i k ≥ q i Q_i^k \geq q_i Qikqi
Q i k ≤ C k Q_i^k \leq C^k QikCk
Q i k ≤ C k + q i k Q_i^k \leq C^k+q_i^k QikCk+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 iV,kK,
e i ≤ B i q ≤ l i e_i \leq B_i^q \leq l_i eiBiqli

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 12, 3 → 4 3\to 4 34这种线路的的节点的要满足时间窗口的要求.

    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 kK, ∀ ( 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 kK, ∀ ( 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 BikBjk

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. 添加目标函数
    # 目标函数(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)
4. 更新model
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)之二》

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值