diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..8eeb0e2
Binary files /dev/null and b/.DS_Store differ
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/.DS_Store" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/.DS_Store"
new file mode 100644
index 0000000..72328e7
Binary files /dev/null and "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/.DS_Store" differ
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.DS_Store" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.DS_Store"
new file mode 100644
index 0000000..48f42b5
Binary files /dev/null and "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.DS_Store" differ
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.gitignore" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.gitignore"
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.gitignore"
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.name" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.name"
new file mode 100644
index 0000000..a645928
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/.name"
@@ -0,0 +1 @@
+GA.py
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/VRPTW.iml" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/VRPTW.iml"
new file mode 100644
index 0000000..77aa2d9
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/VRPTW.iml"
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/inspectionProfiles/profiles_settings.xml" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/inspectionProfiles/profiles_settings.xml"
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/inspectionProfiles/profiles_settings.xml"
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/misc.xml" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/misc.xml"
new file mode 100644
index 0000000..f08d37a
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/misc.xml"
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/modules.xml" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/modules.xml"
new file mode 100644
index 0000000..d123705
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/modules.xml"
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/other.xml" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/other.xml"
new file mode 100644
index 0000000..2e75c2e
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/.idea/other.xml"
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/GA.py" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/GA.py"
new file mode 100644
index 0000000..b0988f6
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/GA.py"
@@ -0,0 +1,180 @@
+import random
+import matplotlib.pyplot as plt
+import numpy as np
+from collections import defaultdict, deque
+from deap import base, creator, tools, algorithms
+
+# 假设设置
+centersNum = 1 # 配送中心数量
+customerNum = 5 # 卸货点数量
+ordersMax = 5 # 每个时间段最大订单数
+timeInterval = 60 # 时间间隔(分钟)
+deliveryMax = 20 # 无人机飞行距离限制(公里)
+loadMax = 3 # 无人机最大负载(物品数)
+speed = 60 # 无人机速度(公里/小时)
+
+# 遗传算法设置
+POP_SIZE = 50 # 种群大小
+GEN = 30 # 迭代代数
+CX_PB = 0.7 # 交叉概率
+MUT_PB = 0.2 # 变异概率
+
+
+# 生成随机位置
+def generatePosition(count, size1, size2):
+ return [(random.randint(size1, size2), random.randint(size1, size2)) for _ in range(count)]
+
+# 位置距离计算
+def distance(pos1, pos2):
+ return ((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2) ** 0.5
+
+# 生成的位置
+centers = generatePosition(centersNum, 3, 7)
+customers = generatePosition(customerNum, 0, 10)
+
+
+# 订单类
+class Order:
+ def __init__(self, priority, customerId, generateTime):
+ self.priority = priority # 1: 一般, 2: 较紧急, 3: 紧急
+ self.customerId = customerId
+ self.generateTime = generateTime
+ self.deadline = self.setDeadline(generateTime, priority)
+ self.processed = False
+
+ def setDeadline(self, generateTime, priority):
+ if priority == 1:
+ return generateTime + 180 # 一般,3小时内送达
+ elif priority == 2:
+ return generateTime + 90 # 较紧急,1.5小时内送达
+ else:
+ return generateTime + 30 # 紧急,0.5小时内送达
+
+# 生成订单
+def generateOrders(time):
+ orderNum = random.randint(0, ordersMax)
+ return [Order(random.randint(1, 3), random.randint(0, customerNum - 1), time) for _
+ in range(orderNum)]
+
+def createRoute(individual, center, customers):
+ # 根据遗传算法个体生成路径
+ route = [center] + [customers[i] for i in individual] + [center]
+ return route
+
+def evalRoute(individual, center, customers):
+ # 计算路径的总距离
+ route = createRoute(individual, center, customers)
+ total_distance = sum(distance(route[i], route[i + 1]) for i in range(len(route) - 1))
+ return (total_distance,)
+
+def setupGA(center, customerIds, customers):
+ creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
+ creator.create("Individual", list, fitness=creator.FitnessMin)
+
+ toolbox = base.Toolbox()
+ # 确保随机抽样的元素数量不超过列表大小
+ sample_size = min(len(customerIds), len(customerIds))
+ toolbox.register("indices", random.sample, range(len(customerIds)), sample_size)
+ toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.indices)
+ toolbox.register("population", tools.initRepeat, list, toolbox.individual)
+ toolbox.register("evaluate", evalRoute, center=center, customers=customers)
+ toolbox.register("mate", tools.cxOrdered)
+ toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.05)
+ toolbox.register("select", tools.selTournament, tournsize=3)
+
+ return toolbox
+
+def optimizeRoutes(center, orders, customers):
+ customerIds = [order.customerId for order in orders if not order.processed]
+ if len(customerIds) < 2: # 如果小于2个订单,则无法进行有效的路径规划
+ return [center] + [customers[idx] for idx in customerIds] + [center]
+
+ toolbox = setupGA(center, customerIds, customers)
+ pop = toolbox.population(n=POP_SIZE)
+ hof = tools.HallOfFame(1, similar=np.array_equal)
+
+ stats = tools.Statistics(lambda ind: ind.fitness.values)
+ stats.register("min", np.min)
+ stats.register("avg", np.mean)
+
+ algorithms.eaSimple(pop, toolbox, cxpb=CX_PB, mutpb=MUT_PB, ngen=GEN, stats=stats, halloffame=hof, verbose=False)
+
+ bestRoute = createRoute(hof[0], center, customers)
+ for idx in hof[0]:
+ orders[idx].processed = True
+ return bestRoute
+
+
+def selectDrones(orders, centers, customers):
+ paths = []
+ orders_by_center = defaultdict(list)
+
+ for order in orders:
+ if order.processed:
+ continue
+ customerPos = customers[order.customerId]
+ nearest_center = min(centers, key=lambda x: distance(x, customerPos))
+ orders_by_center[nearest_center].append(order)
+
+ for center, center_orders in orders_by_center.items():
+ if center_orders:
+ best_path = optimizeRoutes(center, center_orders, customers)
+ paths.append(best_path)
+ return paths
+
+# 绘图
+def plot(centers, customers, paths):
+ plt.figure(figsize=(10, 8))
+ colors = plt.cm.viridis(np.linspace(0, 1, len(paths)))
+
+ center_plotted, drop_plotted = False, False
+
+ for x, y in centers:
+ if not center_plotted:
+ plt.scatter(x, y, c='blue', alpha=1, marker=',', linewidths=3)
+ center_plotted = True
+ else:
+ plt.scatter(x, y, c='blue', alpha=1, marker=',', linewidths=3)
+
+ for x, y in customers:
+ if not drop_plotted:
+ plt.scatter(x, y, c='red', alpha=1, marker='o', linewidths=3)
+ drop_plotted = True
+ else:
+ plt.scatter(x, y, c='red', alpha=1, marker='o', linewidths=3)
+
+ if paths: # 确保路径列表不为空
+ for idx, path in enumerate(paths):
+ # 期望路径格式:路径是一个包含起点、途径点和终点的列表
+ print(f"Path {idx + 1}: {path}")
+ plt.plot([p[0] for p in path], [p[1] for p in path],
+ color=colors[idx], linestyle='--')
+
+ plt.legend(loc='best')
+ plt.title('Drone Delivery Routing')
+ plt.grid(False)
+ plt.show()
+
+# 使用改进的路径选择逻辑
+def simulation(duration):
+ orders = []
+ time = 0
+ paths = []
+
+ while time < duration:
+ new_orders = generateOrders(time)
+ orders.extend(new_orders)
+
+ new_paths = selectDrones(orders, centers, customers)
+ paths.extend(new_paths)
+
+ plot(centers, customers, paths)
+ plt.pause(1)
+
+ time += timeInterval
+
+ plt.show()
+
+
+# 运行模拟
+simulation(240)
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/LP.py" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/LP.py"
new file mode 100644
index 0000000..c991c57
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/VRPTW\357\274\210\346\272\220\347\240\201\357\274\211/LP.py"
@@ -0,0 +1,672 @@
+# Description: Python 调用 Gurobi 建模求解 VRPTW 问题
+import time
+import matplotlib.pyplot as plt
+import numpy as np
+from gurobipy import *
+
+
+class Data:
+ customerNum = 0
+ nodeNum = 0
+ vehicleNum = 0
+ capacity = 0
+ corX = []
+ corY = []
+ demand = []
+ serviceTime = []
+ readyTime = []
+ dueTime = []
+ distanceMatrix = [[]]
+
+
+def readData(path, customerNum):
+ data = Data()
+ data.customerNum = customerNum
+ if customerNum is not None:
+ data.nodeNum = customerNum + 6
+
+ with open(path, 'r') as f:
+ lines = f.readlines()
+ count = 0
+ for line in lines:
+ count += 1
+ if count == 5:
+ line = line[:-1]
+ s = re.split(r" +", line)
+ data.vehicleNum = int(s[1])
+ data.capacity = float(s[2])
+ elif count >= 10 and (customerNum is None or count <= 12 + customerNum):
+ line = line[:-1]
+ s = re.split(r" +", line)
+ data.corX.append(float(s[2]))
+ data.corY.append(float(s[3]))
+ data.demand.append(float(s[4]))
+ data.readyTime.append(float(s[5]))
+ data.dueTime.append(float(s[6]))
+ data.serviceTime.append(float(s[7]))
+ data.nodeNum = len(data.corX) + 3
+
+ data.customerNum = data.nodeNum - 6
+
+
+ # 回路
+ data.corX.append(data.corX[0])
+ data.corY.append(data.corY[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])
+ data.corX.append(data.corX[1])
+ data.corY.append(data.corY[1])
+ data.demand.append(data.demand[1])
+ data.readyTime.append(data.readyTime[1])
+ data.dueTime.append(data.dueTime[1])
+ data.serviceTime.append(data.serviceTime[1])
+ data.corX.append(data.corX[2])
+ data.corY.append(data.corY[2])
+ data.demand.append(data.demand[2])
+ data.readyTime.append(data.readyTime[2])
+ data.dueTime.append(data.dueTime[2])
+ data.serviceTime.append(data.serviceTime[2])
+
+ # 计算距离矩阵
+ data.distanceMatrix = np.zeros((data.nodeNum, data.nodeNum))
+ for i in range(data.nodeNum):
+ for j in range(i + 1, data.nodeNum):
+ distance = math.sqrt((data.corX[i] - data.corX[j]) ** 2 + (data.corY[i] - data.corY[j]) ** 2)
+ data.distanceMatrix[i][j] = data.distanceMatrix[j][i] = distance
+ return data
+
+
+class Solution:
+ ObjVal = 0
+ X = [[]]
+ S = [[]]
+ routes = [[]]
+ routeNum = 0
+
+ def __init__(self, data, model):
+ self.ObjVal = model.ObjVal
+ # X_ijk
+ self.X = [[([0] * data.vehicleNum) for _ in range(data.nodeNum)] for _ in range(data.nodeNum)]
+ # S_ik
+ self.S = [([0] * data.vehicleNum) for _ in range(data.nodeNum)]
+ # routes
+ self.routes = []
+
+
+def getSolution(data, model):
+ solution = Solution(data, model) # 初始化 Solution 对象
+
+ for m in model.getVars(): # 遍历所有变量
+ split_arr = re.split(r"_", m.VarName) # 分割变量名
+ if split_arr[0] == 'X' and m.x > 0.5: # 检查是否为 X_ijk 变量且其值大于 0.5
+ solution.X[int(split_arr[1])][int(split_arr[2])][int(split_arr[3])] = m.x # 更新 solution 中的 X 变量值
+ elif split_arr[0] == 'S' and m.x > 0.5: # 检查是否为 S_ik 变量且其值大于 0.5
+ solution.S[int(split_arr[1])][int(split_arr[2])] = m.x # 更新 solution 中的 S 变量值
+ for k in range(data.vehicleNum): # 遍历每辆车
+ i = 0 # 初始节点(通常开始节点是0)
+ subRoute = [] # 用于存储当前车辆路径的子路线
+ subRoute.append(i) # 将起点加入路径
+ finish = False # 设置结束标志,初始为False,表示路径尚未完成
+
+ while not finish: # 当路径未完成时,继续循环
+ for j in range(data.nodeNum): # 遍历所有节点,寻找下一个线路
+ if solution.X[i][j][k] > 0.5: # 如果车辆从i到j,是最优解中的一条路径
+ subRoute.append(j) # 将节点j加入路径
+ i = j # 更新当前节点为j
+ if j == data.nodeNum - 1: # 如果到达终点节点,路径结束
+ finish = True
+
+ if len(subRoute) >= 3: # 确保路径至少经过了两个节点(起点和所有访问的节点)
+ subRoute[-1] = 0 # 将子路径的最后一个节点变为起点(假设回到出发点)
+ solution.routes.append(subRoute) # 将该子路径加入solution.routes
+ solution.routeNum += 1 # 更新路径数
+ return solution
+def getSolution0(data, model):
+ solution = Solution(data, model) # 初始化 Solution 对象
+
+ for m in model.getVars(): # 遍历所有变量
+ split_arr = re.split(r"_", m.VarName) # 分割变量名
+ if split_arr[0] == 'X' and m.x > 0.5: # 检查是否为 X_ijk 变量且其值大于 0.5
+ solution.X[int(split_arr[1])][int(split_arr[2])][int(split_arr[3])] = m.x # 更新 solution 中的 X 变量值
+ elif split_arr[0] == 'S' and m.x > 0.5: # 检查是否为 S_ik 变量且其值大于 0.5
+ solution.S[int(split_arr[1])][int(split_arr[2])] = m.x # 更新 solution 中的 S 变量值
+ for k in range(data.vehicleNum): # 遍历每辆车
+ i = 0 # 初始节点(通常开始节点是0)
+ subRoute = [] # 用于存储当前车辆路径的子路线
+ subRoute.append(i) # 将起点加入路径
+ finish = False # 设置结束标志,初始为False,表示路径尚未完成
+
+ while not finish: # 当路径未完成时,继续循环
+ for j in r0: # 遍历所有节点,寻找下一个线路
+ if solution.X[i][j][k] > 0.5: # 如果车辆从i到j,是最优解中的一条路径
+ subRoute.append(j) # 将节点j加入路径
+ i = j # 更新当前节点为j
+ if j == r0[-1]: # 如果到达终点节点,路径结束
+ finish = True
+
+ if len(subRoute) >= 3: # 确保路径至少经过了两个节点(起点和所有访问的节点)
+ subRoute[-1] = 0 # 将子路径的最后一个节点变为起点(假设回到出发点)
+ solution.routes.append(subRoute) # 将该子路径加入solution.routes
+ solution.routeNum += 1 # 更新路径数
+ return solution
+def getSolution1(data, model):
+ solution = Solution(data, model) # 初始化 Solution 对象
+
+ for m in model.getVars(): # 遍历所有变量
+ split_arr = re.split(r"_", m.VarName) # 分割变量名
+ if split_arr[0] == 'X' and m.x > 0.5: # 检查是否为 X_ijk 变量且其值大于 0.5
+ solution.X[int(split_arr[1])][int(split_arr[2])][int(split_arr[3])] = m.x # 更新 solution 中的 X 变量值
+ elif split_arr[0] == 'S' and m.x > 0.5: # 检查是否为 S_ik 变量且其值大于 0.5
+ solution.S[int(split_arr[1])][int(split_arr[2])] = m.x # 更新 solution 中的 S 变量值
+ for k in range(data.vehicleNum): # 遍历每辆车
+ i = 1 # 初始节点(通常开始节点是0)
+ subRoute = [] # 用于存储当前车辆路径的子路线
+ subRoute.append(i) # 将起点加入路径
+ finish = False # 设置结束标志,初始为False,表示路径尚未完成
+
+ while not finish: # 当路径未完成时,继续循环
+ for j in r1: # 遍历所有节点,寻找下一个线路
+ if solution.X[i][j][k] > 0.5: # 如果车辆从i到j,是最优解中的一条路径
+ subRoute.append(j) # 将节点j加入路径
+ i = j # 更新当前节点为j
+ if j == r1[-1]: # 如果到达终点节点,路径结束
+ finish = True
+
+ if len(subRoute) >= 3: # 确保路径至少经过了两个节点(起点和所有访问的节点)
+ subRoute[-1] = 1 # 将子路径的最后一个节点变为起点(假设回到出发点)
+ solution.routes.append(subRoute) # 将该子路径加入solution.routes
+ solution.routeNum += 1 # 更新路径数
+ return solution
+
+def getSolution2(data, model):
+ solution = Solution(data, model) # 初始化 Solution 对象
+
+ for m in model.getVars(): # 遍历所有变量
+ split_arr = re.split(r"_", m.VarName) # 分割变量名
+ if split_arr[0] == 'X' and m.x > 0.5: # 检查是否为 X_ijk 变量且其值大于 0.5
+ solution.X[int(split_arr[1])][int(split_arr[2])][int(split_arr[3])] = m.x # 更新 solution 中的 X 变量值
+ elif split_arr[0] == 'S' and m.x > 0.5: # 检查是否为 S_ik 变量且其值大于 0.5
+ solution.S[int(split_arr[1])][int(split_arr[2])] = m.x # 更新 solution 中的 S 变量值
+ for k in range(data.vehicleNum): # 遍历每辆车
+ i = 2 # 初始节点(通常开始节点是0)
+ subRoute = [] # 用于存储当前车辆路径的子路线
+ subRoute.append(i) # 将起点加入路径
+ finish = False # 设置结束标志,初始为False,表示路径尚未完成
+
+ while not finish: # 当路径未完成时,继续循环
+ for j in r2: # 遍历所有节点,寻找下一个线路
+ if solution.X[i][j][k] > 0.5: # 如果车辆从i到j,是最优解中的一条路径
+ subRoute.append(j) # 将节点j加入路径
+ i = j # 更新当前节点为j
+ if j == r2[-1]: # 如果到达终点节点,路径结束
+ finish = True
+
+ if len(subRoute) >= 3: # 确保路径至少经过了两个节点(起点和所有访问的节点)
+ subRoute[-1] = 2 # 将子路径的最后一个节点变为起点(假设回到出发点)
+ solution.routes.append(subRoute) # 将该子路径加入solution.routes
+ solution.routeNum += 1 # 更新路径数
+ return solution
+def plot_solution(solution, customer_num):
+ # 设置绘图框的标题和坐标标签
+ plt.xlabel("x")
+ plt.ylabel("y")
+ plt.title(f"{customer_num} Customers") # 标题显示数据类型和顾客数量
+
+ # 绘制起点
+ plt.scatter(data.corX[0], data.corY[0], c='blue', alpha=1, marker=',', linewidths=3, label='depot') # 起点
+ plt.scatter(data.corX[1], data.corY[1], c='blue', alpha=1, marker=',', linewidths=3, label='depot') # 起点
+ plt.scatter(data.corX[2], data.corY[2], c='blue', alpha=1, marker=',', linewidths=3, label='depot') # 起点
+
+ # 绘制客户点
+ plt.scatter(data.corX[3:-3], data.corY[3:-3], c='red', alpha=1, marker='o', linewidths=3,
+ label='customer') # 普通站点
+
+ # 遍历每个路径,并绘制路径上的每一段弧
+ for k in range(solution.routeNum):
+ for i in range(len(solution.routes[k]) - 1):
+ a = solution.routes[k][i]
+ b = solution.routes[k][i + 1]
+ x = [data.corX[a], data.corX[b]]
+ y = [data.corY[a], data.corY[b]]
+ plt.plot(x, y, 'k', linewidth=1) # 使用黑色线绘制路径上的线段
+
+ # 绘制图表
+ plt.grid(False) # 隐藏网格线
+ # plt.legend(loc='best') # 添加图例
+
+
+
+def print_solution(solution, data):
+ for index, subRoute in enumerate(solution.routes):
+ distance = 0
+ load = 0
+ for i in range(len(subRoute) - 1):
+ distance += data.distanceMatrix[subRoute[i]][subRoute[i + 1]]
+ load += data.demand[subRoute[i]]
+ print(f"Route-{index + 1} : {subRoute} , distance: {distance} , load: {load}")
+
+
+def solve(data):
+ # 声明模型
+ model = Model("VRPTW")
+ # 模型设置
+ # 关闭输出
+ model.setParam('OutputFlag', 0)
+ # 定义变量
+ X = [[[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)] for _ in range(data.nodeNum)]
+ S = [[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)]
+ for i in range(data.nodeNum):
+ for k in range(data.vehicleNum):
+ S[i][k] = model.addVar(data.readyTime[i], data.dueTime[i], vtype=GRB.CONTINUOUS, name=f'S_{i}_{k}')
+ for j in range(data.nodeNum):
+ X[i][j][k] = model.addVar(vtype=GRB.BINARY, name=f"X_{i}_{j}_{k}")
+ # 目标函数
+ obj = LinExpr(0)
+ for i in range(data.nodeNum):
+ for j in range(data.nodeNum):
+ if i != j:
+ for k in range(data.vehicleNum):
+ obj.addTerms(data.distanceMatrix[i][j], X[i][j][k])
+ model.setObjective(obj, GRB.MINIMIZE)
+ # 约束1:车辆只能从一个点到另一个点
+ for i in range(1, data.nodeNum - 1):
+ expr = LinExpr(0)
+ for j in range(data.nodeNum):
+ if i != j:
+ for k in range(data.vehicleNum):
+ if i != 0 and i != data.nodeNum - 1:
+ expr.addTerms(1, X[i][j][k])
+ model.addConstr(expr == 1)
+ # 约束2:车辆必须从仓库出发
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for j in range(1, data.nodeNum):
+ expr.addTerms(1, X[0][j][k])
+ model.addConstr(expr == 1)
+ # 约束3:车辆经过一个点就必须离开一个点
+ 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)
+ # 约束4:车辆最终返回仓库
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in range(data.nodeNum - 1):
+ expr.addTerms(1, X[i][data.nodeNum - 1][k])
+ model.addConstr(expr == 1)
+ # 约束5:车辆容量约束
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in range(1, data.nodeNum - 1):
+ for j in range(data.nodeNum):
+ if i != 0 and i != data.nodeNum - 1 and i != j:
+ expr.addTerms(data.demand[i], X[i][j][k])
+ model.addConstr(expr <= data.capacity)
+ # 约束6:时间窗约束
+ 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.distanceMatrix[i][j] - S[j][k] <= M - M * X[i][j][k])
+ # 记录求解开始时间
+ start_time = time.time()
+ # 求解
+ model.optimize()
+ if model.status == GRB.OPTIMAL:
+ print("-" * 20, "Solved Successfully", '-' * 20)
+ # 输出求解总用时
+ print(f"Solve Time: {time.time() - start_time} s")
+ print(f"Total Travel Distance: {model.ObjVal}")
+ solution = getSolution(data, model)
+ plot_solution(solution, data.customerNum)
+ print_solution(solution, data)
+ else:
+ print("此题无解")
+def solve0(data):
+ # 声明模型
+ model = Model("VRPTW0")
+ # 模型设置
+ # 关闭输出
+ model.setParam('OutputFlag', 0)
+ # 定义变量
+ X = [[[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)] for _ in range(data.nodeNum)]
+ S = [[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)]
+ for i in r0:
+ for k in range(data.vehicleNum):
+ S[i][k] = model.addVar(data.readyTime[i], data.dueTime[i], vtype=GRB.CONTINUOUS, name=f'S_{i}_{k}')
+ for j in r0:
+ X[i][j][k] = model.addVar(vtype=GRB.BINARY, name=f"X_{i}_{j}_{k}")
+ # 目标函数
+ obj = LinExpr(0)
+ for i in r0:
+ for j in r0:
+ if i != j:
+ for k in range(data.vehicleNum):
+ obj.addTerms(data.distanceMatrix[i][j], X[i][j][k])
+ model.setObjective(obj, GRB.MINIMIZE)
+ # 约束1:车辆只能从一个点到另一个点
+ for i in r0[1:-1]:
+ expr = LinExpr(0)
+ for j in r0:
+ if i != j:
+ for k in range(data.vehicleNum):
+ if i != 0 and i != len(r0) - 1:
+ expr.addTerms(1, X[i][j][k])
+ model.addConstr(expr == 1)
+ # 约束2:车辆必须从仓库出发
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for j in r0[1:]:
+ expr.addTerms(1, X[0][j][k])
+ model.addConstr(expr == 1)
+ # 约束3:车辆经过一个点就必须离开一个点
+ for k in range(data.vehicleNum):
+ for h in r0[1:-1]:
+ expr1 = LinExpr(0)
+ expr2 = LinExpr(0)
+ for i in r0:
+ if h != i:
+ expr1.addTerms(1, X[i][h][k])
+ for j in r0:
+ if h != j:
+ expr2.addTerms(1, X[h][j][k])
+ model.addConstr(expr1 == expr2)
+ # 约束4:车辆最终返回仓库
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r0[:-1]:
+ expr.addTerms(1, X[i][r0[-1]][k])
+ model.addConstr(expr == 1)
+ # 约束5:车辆容量约束
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r0[1:-1]:
+ for j in r0:
+ if i != 0 and i != data.nodeNum - 1 and i != j:
+ expr.addTerms(data.demand[i], X[i][j][k])
+ model.addConstr(expr <= data.capacity)
+ # 约束6:时间窗约束
+ for k in range(data.vehicleNum):
+ for i in r0:
+ for j in r0:
+ if i != j:
+ model.addConstr(S[i][k] + data.distanceMatrix[i][j] - S[j][k] <= M - M * X[i][j][k])
+ # 约束7:
+ # 为每个车辆k添加约束条件
+ for k in range(data.vehicleNum):
+ for i in r0:
+ for j in r0:
+ if data.distanceMatrix[i][j] < 20:
+ # 只有当弧ij的长度小于20时,才需要添加约束
+ model.addConstr(X[i][j][k] <= 1)
+ else:
+ # 如果弧ij的长度大于或等于20,车辆k不能选择这条弧
+ model.addConstr(X[i][j][k] == 0)
+
+ # 记录求解开始时间
+ start_time = time.time()
+ # 求解
+ model.optimize()
+ if model.status == GRB.OPTIMAL:
+ print("-" * 20, "配送中心0 Solved Successfully", '-' * 20)
+ # 输出求解总用时
+ print(f"Solve Time: {time.time() - start_time} s")
+ print(f"Total Travel Distance: {model.ObjVal}")
+ solution = getSolution0(data, model)
+ print(solution.routes)
+ plot_solution(solution, data.customerNum)
+ print_solution(solution, data)
+ else:
+ print("此题无解")
+def solve1(data):
+ # 声明模型
+ model = Model("VRPTW1")
+ # 模型设置
+ # 关闭输出
+ model.setParam('OutputFlag', 0)
+ # 定义变量
+ X = [[[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)] for _ in range(data.nodeNum)]
+ S = [[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)]
+ for i in r1:
+ for k in range(data.vehicleNum):
+ S[i][k] = model.addVar(data.readyTime[i], data.dueTime[i], vtype=GRB.CONTINUOUS, name=f'S_{i}_{k}')
+ for j in r1:
+ X[i][j][k] = model.addVar(vtype=GRB.BINARY, name=f"X_{i}_{j}_{k}")
+ # 目标函数
+ obj = LinExpr(0)
+ for i in r1:
+ for j in r1:
+ if i != j:
+ for k in range(data.vehicleNum):
+ obj.addTerms(data.distanceMatrix[i][j], X[i][j][k])
+ model.setObjective(obj, GRB.MINIMIZE)
+ # 约束1:车辆只能从一个点到另一个点
+ for i in r1[1:-1]:
+ expr = LinExpr(0)
+ for j in r1:
+ if i != j:
+ for k in range(data.vehicleNum):
+ if i != 0 and i != len(r1) - 1:
+ expr.addTerms(1, X[i][j][k])
+ model.addConstr(expr == 1)
+ # 约束2:车辆必须从仓库出发
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for j in r1[1:]:
+ expr.addTerms(1, X[1][j][k])
+ model.addConstr(expr == 1)
+ # 约束3:车辆经过一个点就必须离开一个点
+ for k in range(data.vehicleNum):
+ for h in r1[1:-1]:
+ expr1 = LinExpr(0)
+ expr2 = LinExpr(0)
+ for i in r1:
+ if h != i:
+ expr1.addTerms(1, X[i][h][k])
+ for j in r1:
+ if h != j:
+ expr2.addTerms(1, X[h][j][k])
+ model.addConstr(expr1 == expr2)
+ # 约束4:车辆最终返回仓库
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r1[:-1]:
+ expr.addTerms(1, X[i][r1[-1]][k])
+ model.addConstr(expr == 1)
+ # 约束5:车辆容量约束
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r1[1:-1]:
+ for j in r1:
+ if i != 0 and i != data.nodeNum - 1 and i != j:
+ expr.addTerms(data.demand[i], X[i][j][k])
+ model.addConstr(expr <= data.capacity)
+ # 约束6:时间窗约束
+ for k in range(data.vehicleNum):
+ for i in r1:
+ for j in r1:
+ if i != j:
+ model.addConstr(S[i][k] + data.distanceMatrix[i][j] - S[j][k] <= M - M * X[i][j][k])
+ # 约束7:
+ # 为每个车辆k添加约束条件
+ for k in range(data.vehicleNum):
+ for i in r1:
+ for j in r1:
+ if data.distanceMatrix[i][j] < 20:
+ # 只有当弧ij的长度小于20时,才需要添加约束
+ model.addConstr(X[i][j][k] <= 1)
+ else:
+ # 如果弧ij的长度大于或等于20,车辆k不能选择这条弧
+ model.addConstr(X[i][j][k] == 0)
+ # 记录求解开始时间
+ start_time = time.time()
+ # 求解
+ model.optimize()
+ if model.status == GRB.OPTIMAL:
+ print("-" * 20, "配送中心1 Solved Successfully", '-' * 20)
+ # 输出求解总用时
+ print(f"Solve Time: {time.time() - start_time} s")
+ print(f"Total Travel Distance: {model.ObjVal}")
+ solution = getSolution1(data, model)
+ print(solution.routes)
+ plot_solution(solution, data.customerNum)
+ print_solution(solution, data)
+ else:
+ print("此题无解")
+def solve2(data):
+ # 声明模型
+ model = Model("VRPTW2")
+ # 模型设置
+ # 关闭输出
+ model.setParam('OutputFlag', 0)
+ # 定义变量
+ X = [[[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)] for _ in range(data.nodeNum)]
+ S = [[[] for _ in range(data.vehicleNum)] for _ in range(data.nodeNum)]
+ for i in r2:
+ for k in range(data.vehicleNum):
+ S[i][k] = model.addVar(data.readyTime[i], data.dueTime[i], vtype=GRB.CONTINUOUS, name=f'S_{i}_{k}')
+ for j in r2:
+ X[i][j][k] = model.addVar(vtype=GRB.BINARY, name=f"X_{i}_{j}_{k}")
+ # 目标函数
+ obj = LinExpr(0)
+ for i in r2:
+ for j in r2:
+ if i != j:
+ for k in range(data.vehicleNum):
+ obj.addTerms(data.distanceMatrix[i][j], X[i][j][k])
+ model.setObjective(obj, GRB.MINIMIZE)
+ # 约束1:车辆只能从一个点到另一个点
+ for i in r2[1:-1]:
+ expr = LinExpr(0)
+ for j in r2:
+ if i != j:
+ for k in range(data.vehicleNum):
+ if i != 0 and i != len(r2) - 1:
+ expr.addTerms(1, X[i][j][k])
+ model.addConstr(expr == 1)
+ # 约束2:车辆必须从仓库出发
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for j in r2[1:]:
+ expr.addTerms(1, X[2][j][k])
+ model.addConstr(expr == 1)
+ # 约束3:车辆经过一个点就必须离开一个点
+ for k in range(data.vehicleNum):
+ for h in r2[1:-1]:
+ expr1 = LinExpr(0)
+ expr2 = LinExpr(0)
+ for i in r2:
+ if h != i:
+ expr1.addTerms(1, X[i][h][k])
+ for j in r2:
+ if h != j:
+ expr2.addTerms(1, X[h][j][k])
+ model.addConstr(expr1 == expr2)
+ # 约束4:车辆最终返回仓库
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r2[:-1]:
+ expr.addTerms(1, X[i][r2[-1]][k])
+ model.addConstr(expr == 1)
+ # 约束5:车辆容量约束
+ for k in range(data.vehicleNum):
+ expr = LinExpr(0)
+ for i in r2[1:-1]:
+ for j in r2:
+ if i != 0 and i != data.nodeNum - 1 and i != j:
+ expr.addTerms(data.demand[i], X[i][j][k])
+ model.addConstr(expr <= data.capacity)
+ # 约束6:时间窗约束
+ for k in range(data.vehicleNum):
+ for i in r2:
+ for j in r2:
+ if i != j:
+ model.addConstr(S[i][k] + data.distanceMatrix[i][j] - S[j][k] <= M - M * X[i][j][k])
+ # 约束7:
+ # 为每个车辆k添加约束条件
+ for k in range(data.vehicleNum):
+ for i in r2:
+ for j in r2:
+ if data.distanceMatrix[i][j] < 20:
+ # 只有当弧ij的长度小于20时,才需要添加约束
+ model.addConstr(X[i][j][k] <= 1)
+ else:
+ # 如果弧ij的长度大于或等于20,车辆k不能选择这条弧
+ model.addConstr(X[i][j][k] == 0)
+
+ # 记录求解开始时间
+ start_time = time.time()
+ # 求解
+ model.optimize()
+ if model.status == GRB.OPTIMAL:
+ print("-" * 20, "配送中心2 Solved Successfully", '-' * 20)
+ # 输出求解总用时
+ print(f"Solve Time: {time.time() - start_time} s")
+ print(f"Total Travel Distance: {model.ObjVal}")
+ solution = getSolution2(data, model)
+ print(solution.routes)
+ plot_solution(solution, data.customerNum)
+ print_solution(solution, data)
+ else:
+ print("此题无解")
+
+
+
+
+
+if __name__ == '__main__':
+ r0 = []
+ r1 = []
+ r2 = []
+
+ # 数据集路径
+ data_path = '../spots(问题实例数据).txt'
+ # 顾客个数设置(从上往下读取完 customerNum 个顾客为止,例如c101文件中有100个顾客点,
+ # 但是跑100个顾客点太耗时了,设置这个数是为了只选取一部分顾客点进行计算,用来快速测试算法)
+ # 如果想用完整的顾客点进行计算,设置为None即可
+ customerNum = 24
+
+
+ # 一个很大的正数
+ M = 10000000
+ # 读取数据
+ data = readData(data_path, customerNum)
+
+ r0.append(0)
+ r1.append(1)
+ r2.append(2)
+ for i in range(customerNum):
+ if data.distanceMatrix[0][3+i]<=data.distanceMatrix[1][3+i] and data.distanceMatrix[0][3+i]<=data.distanceMatrix[2][3+i]:
+ r0.append(3+i)
+ elif data.distanceMatrix[1][3+i]<=data.distanceMatrix[0][3+i] and data.distanceMatrix[1][3+i]<=data.distanceMatrix[2][3+i]:
+ r1.append(3+i)
+ elif data.distanceMatrix[2][3+i]<=data.distanceMatrix[0][3+i] and data.distanceMatrix[2][3+i]<=data.distanceMatrix[1][3+i]:
+ r2.append(3+i)
+ r0.append(data.nodeNum-3)
+ r1.append(data.nodeNum-2)
+ r2.append(data.nodeNum-1)
+ print(f"配送中心0:{r0}")
+ print(f"配送中心1:{r1}")
+ print(f"配送中心2:{r2}")
+
+
+ # 输出相关数据
+ print("-" * 20, "Problem Information", '-' * 20)
+ print(f'Node Num: {data.nodeNum}')
+ print(f'Customer Num: {data.customerNum}')
+ print(f'Vehicle Num: {data.vehicleNum}')
+ print(f'Vehicle Capacity: {data.capacity}')
+ # 建模求解
+ solve0(data)
+ solve1(data)
+ solve2(data)
+ plt.show() # 显示图表
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/spots\357\274\210\351\227\256\351\242\230\345\256\236\344\276\213\346\225\260\346\215\256\357\274\211.txt" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/spots\357\274\210\351\227\256\351\242\230\345\256\236\344\276\213\346\225\260\346\215\256\357\274\211.txt"
new file mode 100644
index 0000000..b1e43f9
--- /dev/null
+++ "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/spots\357\274\210\351\227\256\351\242\230\345\256\236\344\276\213\346\225\260\346\215\256\357\274\211.txt"
@@ -0,0 +1,36 @@
+
+
+VEHICLE
+NUMBER CAPACITY
+ 100 500
+
+CUSTOMER
+CUST NO. XCOORD. YCOORD. DEMAND READY TIME DUE DATE SERVICE TIME
+
+ 0 40 50 0 0 360 0
+ 1 20 40 0 0 360 0
+ 2 37 20 0 0 360 0
+ 3 40 45 13 0 180 0
+ 4 55 20 19 0 180 0
+ 5 15 30 26 0 90 0
+ 6 25 30 3 0 90 0
+ 7 20 50 5 0 180 0
+ 8 10 43 9 0 30 0
+ 9 45 60 16 0 90 0
+ 10 30 60 16 0 90 0
+ 11 35 65 12 0 90 0
+ 12 50 50 19 0 90 0
+ 13 30 25 23 0 180 0
+ 14 40 10 20 0 180 0
+ 15 30 5 8 0 180 0
+ 16 10 20 19 0 30 0
+ 17 5 30 2 0 30 0
+ 18 30 40 12 0 90 0
+ 19 15 60 17 0 90 0
+ 20 45 65 9 0 180 0
+ 21 45 22 11 0 180 0
+ 22 45 10 18 0 90 0
+ 23 55 5 29 0 90 0
+ 24 40 35 3 0 180 0
+ 25 65 20 6 0 30 0
+ 26 45 30 17 0 90 0
\ No newline at end of file
diff --git "a/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225.pdf" "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225.pdf"
new file mode 100644
index 0000000..81affe9
Binary files /dev/null and "b/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225/\346\262\210\351\235\231\350\277\234-2023202210156-\351\253\230\347\272\247\347\256\227\346\263\225.pdf" differ