基于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库,方便进行遗传算法的实现和可视化操作。

基于NSGA2算法的车辆路径规划问题求解及可视化

原文地址: https://www.cveoy.top/t/topic/necl 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录