基于NSGA2算法的VRP模型求解及可视化

由于该问题涉及到多个约束和目标函数,需要使用多目标优化算法,本文选用NSGA2算法来解决该问题。以下是完整代码:

import numpy as np
import geatpy as ea
import matplotlib.pyplot as plt
import seaborn as sns

# 定义目标函数类
class VRPProblem(ea.Problem):             
    def __init__(self):
        name = 'VRPProblem'            # 初始化函数名称
        M = 2                          # 两个目标函数
        maxormins = [1, 1]             # 两个目标函数均为最小化问题
        Dim = 100 * 2 + 10 + 10        # 10辆车,每辆车最多服务10个客户,每个客户有横纵坐标和需求量
        varTypes = [1] * Dim           # 所有变量均为实数变量
        lb = [0] * Dim                 # 所有变量的下界为0
        ub = [50] * 200 + [10] * 10    # 前200个变量为横纵坐标,后面20个变量为需求量和预期送达时间
        lbin = [1] * Dim               # 所有变量的取值范围均为闭区间
        ubin = [1] * Dim
        self.customer_num = 100        # 客户数量
        self.vehicle_num = 10          # 车辆数量
        self.capacity = 100            # 车辆容量
        self.speed = 40                # 车辆速度
        self.start_time = 8            # 回到仓库的最迟时间
        self.fixed_cost = 500          # 每辆车的固定成本
        self.distance_cost = 0.05      # 路径行驶成本与行驶距离成正比的比例系数
        self.customers = np.zeros((self.customer_num, 3))  # 客户信息(坐标、需求量、预期送达时间)
        self.customers[:, :2] = np.random.rand(self.customer_num, 2) * 50
        self.customers[:, 2] = np.random.randint(2, 11, self.customer_num)
        self.customers[:, 3] = np.random.rand(self.customer_num) * 8
        self.customers = self.customers.astype(int)
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)  # 调用父类构造方法

    # 计算每个客户的满意度
    def calc_satisfaction(self, routes):
        satisfaction = 0
        for i, route in enumerate(routes):
            time = 0
            for j in range(len(route)):
                customerId = route[j]
                customer = self.customers[customerId]
                distance = np.sqrt((customer[0] - time) ** 2 + (customer[1] - time) ** 2)  # 客户到达时间
                time += distance / self.speed  # 更新时间
                satisfaction += (customer[3] - time) * (customer[3] > time) * 0.8 + (time - customer[3]) * (time >= customer[3]) * 1.3
            satisfaction += (self.start_time - time) * (self.start_time > time) * 0.8 + (time - self.start_time) * (time >= self.start_time) * 1.3
        return satisfaction

    # 计算成本
    def calc_cost(self, routes):
        cost = 0
        for i, route in enumerate(routes):
            distance = 0
            time = 0
            for j in range(len(route)):
                customerId = route[j]
                customer = self.customers[customerId]
                distance += np.sqrt((customer[0] - time) ** 2 + (customer[1] - time) ** 2)  # 计算客户到达时间
                time += distance / self.speed  # 更新时间
            distance += np.sqrt((0 - time) ** 2 + (0 - time) ** 2)  # 计算回到仓库的时间
            cost += self.fixed_cost + distance * self.distance_cost  # 计算成本
        return cost

    # 目标函数
    def aimFunc(self, pop):
        x = pop.Phen  # 取出种群所有个体的决策变量
        routes = [[] for i in range(self.vehicle_num)]  # 路径列表
        loads = [0] * self.vehicle_num  # 车辆负载列表
        for i in range(self.customer_num):
            vehicle = np.argmin(loads)  # 找到负载最小的车辆
            if loads[vehicle] + self.customers[i][2] <= self.capacity:  # 判断是否能够装下该客户的货物
                routes[vehicle].append(i)
                loads[vehicle] += self.customers[i][2]
        satisfaction = self.calc_satisfaction(routes)  # 计算客户满意度
        cost = self.calc_cost(routes)  # 计算成本
        pop.ObjV[:, 0] = cost  # 第一个目标函数为总成本最小
        pop.ObjV[:, 1] = -satisfaction  # 第二个目标函数为总满意度最大(注意要取负数)

接下来,我们需要使用NSGA2算法来求解该问题,代码如下:

# 实例化NSGA2算法对象
algorithm = ea.moea.NSGA2_templet(problem=VRPProblem(), population_size=100, offspring_population_size=100, max_iteration=100)

# 运行算法
algorithm.run()

# 输出结果
best_x = algorithm.best_gen.Phen[0]
routes = [[] for i in range(10)]
loads = [0] * 10
for i in range(100):
    vehicle = np.argmin(loads)
    if loads[vehicle] + VRPProblem().customers[i][2] <= 100:
        routes[vehicle].append(i)
        loads[vehicle] += VRPProblem().customers[i][2]

print("使用车辆数:", len([load for load in loads if load > 0]))
print("每辆车的完整配送路线:")
for i in range(len(routes)):
    print("车辆", i + 1, ":仓库 ->", end=" ")
    for customer in routes[i]:
        print(customer + 1, "->", end=" ")
    print("仓库")

# 可视化帕累托前沿
data = algorithm.optima_history
sns.scatterplot(x=data[:, 0], y=-data[:, 1])
plt.xlabel("Total cost")
plt.ylabel("Total satisfaction")
plt.show()

# 可视化车辆配送路径
plt.figure(figsize=(8, 8))
plt.scatter([0], [0], c="blue", s=200, marker="s")
for i in range(10):
    route = routes[i]
    x = [VRPProblem().customers[j][0] for j in route]
    y = [VRPProblem().customers[j][1] for j in route]
    plt.scatter(x, y, c="orange")
    plt.plot(x + [0], y + [0], c="orange")
plt.xlim([0, 50])
plt.ylim([0, 50])
plt.xlabel("X")
plt.ylabel("Y")
plt.show()

运行结果如下:

using pygmo

# 先定义一个可行性函数
def feasible(x):
    loads = [0] * 10
    for i in range(100):
        vehicle = np.argmin(loads)
        if loads[vehicle] + x[i * 2 + 2] <= 100:
            loads[vehicle] += x[i * 2 + 2]
        else:
            return False
    return True

# 然后定义目标函数
def objective(x):
    routes = [[] for i in range(10)]
    loads = [0] * 10
    for i in range(100):
        vehicle = np.argmin(loads)
        if loads[vehicle] + x[i * 2 + 2] <= 100:
            routes[vehicle].append(i)
            loads[vehicle] += x[i * 2 + 2]
    satisfaction = 0
    for i, route in enumerate(routes):
        time = 0
        for j in range(len(route)):
            customerId = route[j]
            customer = customers[customerId]
            distance = np.sqrt((customer[0] - time) ** 2 + (customer[1] - time) ** 2)
            time += distance / 40
            satisfaction += (customer[3] - time) * (customer[3] > time) * 0.8 + (time - customer[3]) * (time >= customer[3]) * 1.3
        satisfaction += (8 - time) * (8 > time) * 0.8 + (time - 8) * (time >= 8) * 1.3
    cost = sum([500 + np.sqrt((customers[route[0]][0] - 0) ** 2 + (customers[route[0]][1] - 0) ** 2) * 0.05 +             sum([np.sqrt((customers[route[j]][0] - customers[route[j - 1]][0]) ** 2 + (customers[route[j]][1] - customers[route[j - 1]][1]) ** 2) * 0.05 for j in range(1, len(route))]) +             np.sqrt((0 - customers[route[-1]][0]) ** 2 + (0 - customers[route[-1]][1]) ** 2) * 0.05 for route in routes])
    return [cost, -satisfaction]

# 然后使用pygmo来求解
algo = pg.algorithm(pg.nsga2(gen=1000))
prob = pg.problem(pg.real(220))
prob.set_bounds(0, 50)
prob.set_bounds(200, 220, 2, 10)
prob.set_bounds(220, 240, 0, 8)
prob.set_feasibility(feasible)
prob.set_evaluate(objective)
pop = pg.population(prob, 100)
pop = algo.evolve(pop)
data = np.array(pop.get_f())
sns.scatterplot(x=data[:, 0], y=-data[:, 1])
plt.xlabel("Total cost")
plt.ylabel("Total satisfaction")
plt.show()

best_x = pop.get_x()[0]
routes = [[] for i in range(10)]
loads = [0] * 10
for i in range(100):
    vehicle = np.argmin(loads)
    if loads[vehicle] + best_x[i * 2 + 2] <= 100:
        routes[vehicle].append(i)
        loads[vehicle] += best_x[i * 2 + 2]

print("使用车辆数:", len([load for load in loads if load > 0]))
print("每辆车的完整配送路线:")
for i in range(len(routes)):
    print("车辆", i + 1, ":仓库 ->", end=" ")
    for customer in routes[i]:
        print(customer + 1, "->", end=" ")
    print("仓库")

plt.figure(figsize=(8, 8))
plt.scatter([0], [0], c="blue", s=200, marker="s")
for i in range(10):
    route = routes[i]
    x = [customers[j][0] for j in route]
    y = [customers[j][1] for j in route]
    plt.scatter(x, y, c="orange")
    plt.plot(x + [0], y + [0], c="orange")
plt.xlim([0, 50])
plt.ylim([0, 50])
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
基于NSGA2算法的VRP模型求解及可视化

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

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