基于python语言,实现经典自适应大邻域算法(ALNS)对车辆路径规划问题(CVRP)进行求解, 优化代码结构,改进Split函数


目录

  • 往期优质资源
  • 1. 适用场景
  • 2. 改进效果对比
  • 2.1 实验结果汇总
  • 2.2 目标函数对比
  • 2.3 求解时间对比
  • 3. 求解结果
  • 4. 部分代码
  • 5. 完整代码
  • 参考


往期优质资源


遗传算法

蚁群算法


禁忌搜索算法


模拟退火算法


自适应大邻域算法


粒子群算法


量子粒子群算法


差分进化算法

MDVRP系列 遗传算法

蚁群算法


禁忌搜索算法


模拟退火算法


自适应大邻域算法


粒子群算法


量子粒子群算法


差分进化算法

VRPTW系列 遗传算法

蚁群算法


禁忌搜索算法


模拟退火算法


自适应大邻域算法


粒子群算法


量子粒子群算法


差分进化算法

HVRP系列 遗传算法

蚁群算法


禁忌搜索算法


模拟退火算法


自适应大邻域算法


粒子群算法


量子粒子群算法


差分进化算法

MDHFVRPTW系列 遗传算法

蚁群算法


禁忌搜索算法


模拟退火算法


自适应大邻域算法


粒子群算法


量子粒子群算法


差分进化算法


1. 适用场景

  • 求解CVRP
  • 车辆类型单一
  • 车辆容量不小于需求节点最大需求
  • 单一车辆基地

2. 改进效果对比

这里做了简单的参数敏感性分析,比较不同参数组合下两个版本code的最优值与求解时间的差异。具体为:1)设定权重衰减系数为0.1,0.3,0.5 三个不同等级;2)设定退火速率为0.7,0.75,0.8,0.85,0.9,0.95等不同值;3)设定权重更新步长为2,4,6,8,10等不同值,其他参数固定不变:最大迭代次数为10,车辆容量为80。

2.1 实验结果汇总

改进后的split算子是基于图论的,相对比较耗时,再加上ALNS的贪婪算子需要进行多次计算,因此改进后的算法在时间上的增加额外明显。

ALNS解决VRP问题Python_算法

2.2 目标函数对比

rho = 0.1:

ALNS解决VRP问题Python_python_02

rho = 0.3:

ALNS解决VRP问题Python_CVRP_03

rho = 0.5:

ALNS解决VRP问题Python_算法_04

2.3 求解时间对比

rho = 0.1:

ALNS解决VRP问题Python_ALNS解决VRP问题Python_05

rho = 0.3:

ALNS解决VRP问题Python_python_06

rho = 0.5:

ALNS解决VRP问题Python_算法_07

3. 求解结果

(1)收敛曲线

ALNS解决VRP问题Python_ALNS解决VRP问题Python_08

(2)车辆路径

ALNS解决VRP问题Python_python_09

(3)输出文件

ALNS解决VRP问题Python_算法_10

4. 部分代码

(1)数据结构

# 数据结构:解
class Sol():
    def __init__(self):
        self.node_no_seq=None # 解的编码
        self.obj=None # 目标函数
        self.action_id=None # 算子id
        self.route_list=None # 解的解码
        self.route_distance = None  # 车辆路径的长度集合
# 数据结构:网络节点
class Demand():
    def __init__(self):
        self.id = 0 # 节点id
        self.x_coord = 0 # 节点平面横坐标
        self.y_coord = 0  # 节点平面纵坐标
        self.demand = 0 # 节点需求
# 数据结构:全局参数
class Model():
    def __init__(self):
        self.best_sol = None # 全局最优解
        self.demand_dict = {}  # 需求节点集合
        self.demand_id_list = []
        self.sol_list = []  # 解的集合
        self.depot = None # 车场节点
        self.number_of_nodes = 0 # 需求节点数量
        self.vehicle_cap = 80 # 车辆最大容量
        self.distance_matrix = {}
        self.popsize = 100 # 种群规模
        self.alpha = 2 # 信息启发式因子
        self.beta = 3 # 期望启发式因子
        self.Q = 100 # 信息素总量
        self.rho = 0.5 # 信息素挥发因子
        self.tau = {} # 弧信息素集合
        self.vehicle_cap=0  # 车辆最大容量

(2)距离矩阵

def calDistanceMatrix(model):
    for i in range(len(model.demand_id_list)):
        f_n = model.demand_id_list[i]
        for j in range(i + 1, len(model.demand_id_list)):
            t_n = model.demand_id_list[j]
            dist = math.sqrt((model.demand_dict[f_n].x_coord - model.demand_dict[t_n].x_coord) ** 2
                             + (model.demand_dict[f_n].y_coord - model.demand_dict[t_n].y_coord) ** 2)
            model.distance_matrix[f_n, t_n] = dist
            model.distance_matrix[t_n, f_n] = dist
            model.tau[f_n, t_n] = 100
            model.tau[t_n, f_n] = 100

        dist = math.sqrt((model.demand_dict[f_n].x_coord - model.depot.x_coord) ** 2
                         + (model.demand_dict[f_n].y_coord - model.depot.y_coord) ** 2)
        model.distance_matrix[f_n, model.depot.id] = dist
        model.distance_matrix[model.depot.id, f_n] = dist

(3)路径提取

def extractRoutes(node_no_seq,P,depot_id):
    route_list = []
    route = []
    p = P[node_no_seq[0]]
    for node_seq in node_no_seq:
        if P[node_seq] == p:
            route.append(node_seq)
        else:
            route.insert(0,depot_id)
            route.append(depot_id)
            route_list.append(route)
            route = [node_seq]
            p = P[node_seq]
    return route_list

(4)破坏算子

# 随机破坏
def createRandomDestory(model):
    d=random.uniform(model.rand_d_min,model.rand_d_max)
    return random.sample(model.node_id_list,int(d*(len(model.node_id_list)-1)))
# 最坏值破坏
def createWorseDestory(model,sol):
    deta_f=[]
    for node_no in sol.node_no_seq:
        node_no_seq_=copy.deepcopy(sol.node_no_seq)
        node_no_seq_.remove(node_no)
        obj,_,_=calObj(node_no_seq_,model)
        deta_f.append(sol.obj-obj)
    sorted_id = sorted(range(len(deta_f)), key=lambda k: deta_f[k], reverse=True)
    d=random.randint(model.worst_d_min,model.worst_d_max)
    return [sol.node_no_seq[i] for i in sorted_id[:d]]

(5)收敛曲线

# 绘制目标函数收敛曲线
def plotObj(obj_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']  #show chinese
    plt.rcParams['axes.unicode_minus'] = False  # Show minus sign
    plt.plot(np.arange(1,len(obj_list)+1),obj_list)
    plt.xlabel('Iterations')
    plt.ylabel('Obj Value')
    plt.grid()
    plt.xlim(1,len(obj_list)+1)
    plt.show()

(6)车辆路径

# 绘制优化车辆路径
def plotRoutes(model):
    for route in model.best_sol.route_list:
        x_coord=[]
        y_coord=[]
        for node_no in route:
            x_coord.append(model.demand_dict[node_no].x_coord)
            y_coord.append(model.demand_dict[node_no].y_coord)
        plt.plot(x_coord,y_coord,marker='s',color='b',linewidth=0.5)
    plt.show()

(7)输出结果

# 输出结果
def outPut(model):
    work=xlsxwriter.Workbook('result.xlsx')
    worksheet=work.add_worksheet()
    worksheet.write(0, 0, 'id')
    worksheet.write(0, 1, 'route')
    worksheet.write(0, 2, 'distance')
    worksheet.write(0, 3, 'total_distance')
    worksheet.write(1,3,model.best_sol.obj)
    for id,route in enumerate(model.best_sol.route_list):
        r=[str(i)for i in route]
        worksheet.write(id + 1, 0, f'v{str(id + 1)}')
        worksheet.write(id + 1, 1, '-'.join(r))
        worksheet.write(id + 1, 2, model.best_sol.route_distance[id])
    work.close()

5. 完整代码

如有错误,欢迎交流。