基于NSGA2算法的车辆路径规划问题求解及可视化
基于NSGA2算法的车辆路径规划问题求解及可视化
本文使用NSGA2算法解决一个车辆路径规划问题(VRP),随机生成一个仓库和一百个客户坐标及配送需求,并设定车辆容量、行驶速度等约束条件。目标函数包括总成本最小化和客户满意度最大化。代码基于geatpy库实现,并提供帕累托前沿和车辆配送路径的可视化结果。
问题描述:
- 随机生成一个仓库和一百个客户坐标及客户配送需求,横纵坐标在0到50以内,需求在2-10之间。
- 假设有足够的同质车辆,车辆容量约束为100,车辆行驶速度为40。
- 在0到8之间随机生成每个客户的预期配送到达时间。
- 目标函数有两个:
- 总成本最小: 成本包括所有使用车辆的固定成本和路径行驶成本,每辆车固定成本为500,路径行驶成本与行驶距离成正比,比例系数是0.05。
- 客户满意度总和最大: 客户满意度是客户期望配送到达时间减去实际到达时间,如果大于零则再乘以0.8,如果小于零则再乘以1.3。
- 每个车辆必须在时刻为8之前返回仓库,车辆从仓库出发时刻为0。
- 输出使用车辆数目,每辆车的完整配送路线,并且可视化帕累托前沿和车辆配送路径。
代码实现:
由于涉及到VRP问题,需要先定义一些辅助函数。
首先,定义一个函数用于计算两个点之间的欧几里得距离:
import math
def distance(x1, y1, x2, y2):
return math.sqrt((x1-x2)**2 + (y1-y2)**2)
接下来,定义一个函数用于计算某个路径的总距离:
def path_distance(path, coordinates):
d = 0
for i in range(len(path)-1):
x1, y1 = coordinates[path[i]]
x2, y2 = coordinates[path[i+1]]
d += distance(x1, y1, x2, y2)
return d
然后,定义一个函数用于计算某个路径的总满意度:
def path_satisfaction(path, coordinates, deadlines, arrival_times):
s = 0
for i in range(1, len(path)-1):
x, y = coordinates[path[i]]
expected_time = deadlines[path[i]]
actual_time = arrival_times[path[i]]
diff = actual_time - expected_time
if diff > 0:
s += 0.8 * diff
else:
s += 1.3 * diff
return s
接下来,定义一个函数用于将一个个体转换成一个VRP方案:
def decode(individual, coordinates, deadlines):
paths = []
path = [0]
capacity = 0
arrival_times = [0] * len(coordinates)
for i in individual:
x, y = coordinates[i]
demand = deadlines[i]
if capacity + demand > 100:
path.append(0)
paths.append(path)
path = [0, i]
capacity = demand
arrival_times[i] = distance(coordinates[path[-2]][0], coordinates[path[-2]][1], x, y) / 40
else:
path.append(i)
capacity += demand
arrival_times[i] = arrival_times[path[-2]] + distance(coordinates[path[-2]][0], coordinates[path[-2]][1], x, y) / 40
path.append(0)
paths.append(path)
return paths, arrival_times
最后,定义一个函数用于计算一个VRP方案的成本:
def evaluate(individual, coordinates, deadlines):
paths, arrival_times = decode(individual, coordinates, deadlines)
total_cost = 500 * len(paths)
for path in paths:
total_cost += path_distance(path, coordinates) * 0.05
total_satisfaction = sum([path_satisfaction(path, coordinates, deadlines, arrival_times) for path in paths])
return [total_cost, -total_satisfaction]
有了这些辅助函数,就可以用geatpy库来解决VRP问题了。完整代码如下:
import math
import geatpy as ea
import numpy as np
# 计算两个点之间的欧几里得距离
def distance(x1, y1, x2, y2):
return math.sqrt((x1-x2)**2 + (y1-y2)**2)
# 计算某个路径的总距离
def path_distance(path, coordinates):
d = 0
for i in range(len(path)-1):
x1, y1 = coordinates[path[i]]
x2, y2 = coordinates[path[i+1]]
d += distance(x1, y1, x2, y2)
return d
# 计算某个路径的总满意度
def path_satisfaction(path, coordinates, deadlines, arrival_times):
s = 0
for i in range(1, len(path)-1):
x, y = coordinates[path[i]]
expected_time = deadlines[path[i]]
actual_time = arrival_times[path[i]]
diff = actual_time - expected_time
if diff > 0:
s += 0.8 * diff
else:
s += 1.3 * diff
return s
# 将一个个体转换成一个VRP方案
def decode(individual, coordinates, deadlines):
paths = []
path = [0]
capacity = 0
arrival_times = [0] * len(coordinates)
for i in individual:
x, y = coordinates[i]
demand = deadlines[i]
if capacity + demand > 100:
path.append(0)
paths.append(path)
path = [0, i]
capacity = demand
arrival_times[i] = distance(coordinates[path[-2]][0], coordinates[path[-2]][1], x, y) / 40
else:
path.append(i)
capacity += demand
arrival_times[i] = arrival_times[path[-2]] + distance(coordinates[path[-2]][0], coordinates[path[-2]][1], x, y) / 40
path.append(0)
paths.append(path)
return paths, arrival_times
# 计算一个VRP方案的成本
def evaluate(individual, coordinates, deadlines):
paths, arrival_times = decode(individual, coordinates, deadlines)
total_cost = 500 * len(paths)
for path in paths:
total_cost += path_distance(path, coordinates) * 0.05
total_satisfaction = sum([path_satisfaction(path, coordinates, deadlines, arrival_times) for path in paths])
return [total_cost, -total_satisfaction]
# VRP问题的NSGA2求解器
class VRPSolver(ea.Problem):
def __init__(self, coordinates, deadlines):
self.coordinates = coordinates
self.deadlines = deadlines
name = 'VRP'
M = 2
maxormins = [-1] * M
Dim = len(coordinates) - 1
varTypes = [1] * Dim
lb = [1] * Dim
ub = [Dim] * Dim
lbin = [1] * Dim
ubin = [1] * Dim
self.max_length = 100
self.min_length = 1
ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)
def aimFunc(self, pop):
for i in range(pop.sizes):
individual = pop.Phen[i, :]
pop.ObjV[i, :] = evaluate(individual, self.coordinates, self.deadlines)
# 随机生成一个仓库和一百个客户坐标及客户配送需求
np.random.seed(1)
coordinates = np.random.randint(0, 51, size=(101, 2))
deadlines = np.random.randint(2, 11, size=101)
# VRP问题的NSGA2求解
solver = VRPSolver(coordinates, deadlines)
algorithm = ea.NSGA2_templet(solver)
algorithm.MAXGEN = 50
algorithm.drawing = 2
pop = algorithm.run()
# 输出使用车辆数目、每辆车的完整配送路线
best_individual = pop.bestIndi()
paths, arrival_times = decode(best_individual.Phen, coordinates, deadlines)
print('使用车辆数目:', len(paths))
for i, path in enumerate(paths):
print('车辆', i+1, '的路线:', ' -> '.join(map(str, path)))
# 可视化帕累托前沿和车辆配送路径
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
# 绘制帕累托前沿
ax2 = ax1.twinx()
ax1.set_xlabel('成本')
ax1.set_ylabel('满意度')
ax2.set_ylabel('个数')
ax1.set_xlim(left=0)
ax1.set_ylim(bottom=0)
ax2.set_ylim(bottom=0)
ax1.plot(pop.ObjV[:,0], pop.ObjV[:,1], '.', color='blue')
ax2.plot(pop.FitnV[:,0], color='orange')
# 绘制车辆配送路径
for path in paths:
x = [coordinates[i,0] for i in path]
y = [coordinates[i,1] for i in path]
ax1.plot(x, y, color='red')
plt.show()
运行结果:
代码运行后,会输出使用车辆数目、每辆车的完整配送路线,并显示帕累托前沿和车辆配送路径的可视化结果。
结论:
使用NSGA2算法可以有效解决车辆路径规划问题,并找到满足多目标约束的帕累托最优解。代码中使用了geatpy库,方便进行遗传算法的实现和可视化操作。
原文地址: https://www.cveoy.top/t/topic/necl 著作权归作者所有。请勿转载和采集!