用PYthon里面的geatpy库解决带车辆容量约束和工作时间约束的单一仓库多车辆VRP问题举例说明给出程序确保可以执行
以下是使用geatpy库解决带车辆容量约束和工作时间约束的单一仓库多车辆VRP问题的示例程序:
import numpy as np
import geatpy as ea
class MyProblem(ea.Problem): # 继承Problem父类
def __init__(self, M=1):
name = 'VRP' # 初始化name(函数名称,可以随意设置)
Dim = 2 # 初始化Dim(决策变量维数)
maxormins = [-1] * Dim # 初始化maxormins(目标最小最大化标记列表,-1最小化列表中各目标函数,1最大化列表中各目标函数)
varTypes = [1] * Dim # 初始化varTypes(决策变量类型,0离散,1连续)
lb = [0, 0] # 决策变量下界
ub = [10, 10] # 决策变量上界
lbin = [1] * Dim # 决策变量下边界是否包含,1包含,0不包含
ubin = [1] * Dim # 决策变量上边界是否包含,1包含,0不包含
self.M = M # 初始化M(目标维数)
self.car_cap = 20 # 车辆容量
self.service_time = 1 # 服务时间
self.customers = [(1, 1, 10), (2, 2, 5), (3, 3, 7), (4, 4, 3)] # 客户信息,格式为(客户编号,横坐标,纵坐标,需求量)
ea.Problem.__init__(self, name, Dim, maxormins, varTypes, lb, ub, lbin, ubin) # 调用父类构造方法
def aimFunc(self, pop): # 目标函数
Vars = pop.Phen # 得到决策变量矩阵
x1 = Vars[:, [0]] # 解码得到x1
y1 = Vars[:, [1]] # 解码得到y1
x2 = np.concatenate([Vars[1:, [0]], Vars[[0], [0]]], axis=0) # 解码得到x2
y2 = np.concatenate([Vars[1:, [1]], Vars[[0], [1]]], axis=0) # 解码得到y2
distance = np.sqrt(np.square(x1 - x2) + np.square(y1 - y2)) # 计算距离矩阵
weights = np.array([customer[2] for customer in self.customers]) # 得到需求量数组
capacity = np.array([self.car_cap] * len(weights)) # 得到车辆容量数组
service_time = np.array([self.service_time] * len(weights)) # 得到服务时间数组
problem = VRP(distance, weights, capacity, service_time, 'time', 'cap') # 创建问题对象
problem.solve() # 解决问题
pop.ObjV = problem.ObjV.reshape(-1, self.M) # 将问题对象的目标函数值赋值给种群pop的ObjV属性
class VRP: # 定义问题类
def __init__(self, distance, weights, capacity, service_time, time_constraint, cap_constraint):
self.distance = distance # 距离矩阵
self.weights = weights # 需求量数组
self.capacity = capacity # 车辆容量数组
self.service_time = service_time # 服务时间数组
self.time_constraint = time_constraint # 时间约束类型,'time'为时间窗约束,'no_time'为无时间窗约束
self.cap_constraint = cap_constraint # 容量约束类型,'cap'为容量约束,'no_cap'为无容量约束
self.n_customers = len(weights) # 客户数目
self.n_vehicles = len(capacity) # 车辆数目
self.x = None # 最优解,格式为(客户编号,车辆编号,顺序)
self.ObjV = None # 目标函数值
def solve(self):
self.x = np.zeros((self.n_customers, self.n_vehicles, self.n_customers)) # 初始化解
self.ObjV = np.zeros((self.n_vehicles, 1)) # 初始化目标函数值
for i in range(self.n_vehicles):
customers = list(range(1, self.n_customers)) # 初始化未访问客户列表
remaining_capacity = self.capacity[i] # 初始化剩余容量
current_location = 0 # 当前位置为仓库
current_time = 0 # 初始时刻为0
while customers:
distances = self.distance[current_location][customers] # 得到距离向量
if self.time_constraint == 'time':
time_windows = np.zeros(len(customers)) # 得到时间窗向量
for j, customer in enumerate(customers):
time_windows[j] = self.customers[customer][3]
else:
time_windows = np.zeros(len(customers)) # 时间窗向量的长度为0
if self.cap_constraint == 'cap':
demands = self.weights[customers] # 得到需求量向量
else:
demands = np.zeros(len(customers)) # 需求量向量的长度为0
problem = CvrpProblem(distances, time_windows, demands, remaining_capacity, self.service_time, current_time) # 创建问题对象
problem.solve() # 解决问题
chosen_customer = customers[problem.x - 1] # 得到被选择的客户编号
self.x[chosen_customer][i][0] = current_location # 修改解
self.x[chosen_customer][i][1] = i
self.x[chosen_customer][i][2] = len(customers) # 按顺序记录客户
self.ObjV[i][0] += problem.ObjV # 累加目标函数值
remaining_capacity -= self.weights[chosen_customer] # 更新剩余容量
current_location = chosen_customer # 更新当前位置
current_time = problem.current_time # 更新当前时刻
customers.remove(chosen_customer) # 从未访问客户列表中移除被选择的客户
class CvrpProblem: # 定义问题类
def __init__(self, distances, time_windows, demands, capacity, service_time, current_time):
self.distances = distances # 距离向量
self.time_windows = time_windows # 时间窗向量
self.demands = demands # 需求量向量
self.capacity = capacity # 容量
self.service_time = service_time # 服务时间
self.current_time = current_time # 当前时刻
self.x = None # 最优解,表示被选择的客户编号
self.ObjV = None # 目标函数值
def solve(self):
model = Model('CVRP') # 创建模型对象
n_customers = len(self.distances) # 客户数目
x = {} # 创建变量字典
for i in range(n_customers):
for j in range(n_customers):
x[i, j] = model.addVar(vtype=GRB.BINARY, name=f'x_{i}_{j}') # 添加变量
for i in range(1, n_customers):
model.addConstr(quicksum(x[i, j] for j in range(n_customers)) == 1) # 约束1
model.addConstr(quicksum(x[j, i] for j in range(n_customers)) == 1) # 约束2
model.addConstr(quicksum(x[0, j] for j in range(n_customers)) == 1) # 约束3
model.addConstr(quicksum(x[i, 0] for i in range(n_customers)) == 1) # 约束4
for i in range(1, n_customers):
for j in range(1, n_customers):
if i != j:
model.addConstr(x[i, j] + x[j, i] <= 1) # 约束5
u = {}
for i in range(n_customers):
u[i] = model.addVar(lb=0, ub=self.capacity, vtype=GRB.CONTINUOUS, name=f'u_{i}') # 添加变量
for i in range(1, n_customers):
for j in range(1, n_customers):
if i != j:
model.addConstr(u[i] - u[j] + n_customers * x[i, j] <= n_customers - 1) # 约束6
for i in range(1, n_customers):
model.addConstr(self.demands[i] + quicksum(self.demands[j] * x[j, i] for j in range(n_customers)) <= u[i]) # 约束7
if self.time_windows.size > 0:
y = {}
for i in range(n_customers):
for j in range(n_customers):
y[i, j] = model.addVar(vtype=GRB.CONTINUOUS, name=f'y_{i}_{j}') # 添加变量
for i in range(n_customers):
model.addConstr(y[i, i] == 0) # 约束8
for i in range(n_customers):
for j in range(n_customers):
if i != j:
model.addConstr(y[i, j] >= self.time_windows[i] + self.service_time + self.distances[i][j] - y[j, i] - n_customers * (1 - x[i, j])) # 约束9
for i in range(1, n_customers):
model.addConstr(y[0, i] <= self.time_windows[i]) # 约束10
model.setObjective(quicksum(self.distances[i][j] * x[i, j] for i in range(n_customers) for j in range(n_customers)), GRB.MINIMIZE) # 设定目标函数
model.setParam('OutputFlag', 0) # 设定不输出求解过程
model.optimize() # 求解模型
if model.status == GRB.Status.OPTIMAL:
self.x = np.zeros(n_customers) # 初始化解
self.ObjV = model.objVal # 初始化目标函数值
for i in range(n_customers):
for j in range(n_customers):
if x[i, j].x > 0.5:
self.x[i] = j # 根据变量值得到被选择的客户编号
self.current_time = y[i, j].x - self.service_time - self.distances[i][j] # 更新当前时刻
break
if __name__ == '__main__':
problem = MyProblem() # 创建问题对象
algorithm = ea.soea_DE_rand_1_bin_templet(problem, population_size=50) # 创建算法模板对象
algorithm.run(500) # 执行算法
print(problem.getBest()) # 输出最优解
本程序使用的是geatpy库提供的差分进化算法模板,可以通过调用ea.soea_DE_rand_1_bin_templet()方法来创建算法模板对象。在算法模板对象创建后,可以通过调用run()方法来执行算法,其中的参数指定了算法的迭代次数。最后,可以通过调用getBest()方法来获取最优解。
原文地址: https://www.cveoy.top/t/topic/bM6i 著作权归作者所有。请勿转载和采集!