多配送中心VRP建模与求解—基于粒子群算法

本文探讨了多配送中心的车辆路径问题(VRP),通过粒子群算法进行求解。研究了如何将客户点分配给各个配送中心并优化配送路径,以最小化总成本。提供了具体的算法实现及案例分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多配送中心VRP建模与求解—基于粒子群算法

1 多配送中心VRP

多配送中心的车辆路径问题是 经典VRP 的扩展,它研究的是有多个车场同时对若干个客户进行服务,每个客户都有一定的货物需求。所要确定的是客户点由哪个配送中心服务,并对服务时的车辆路径进行优化,以便达到成本最低、时间最短等目标。

2 课题场景设计

2.1 场景

单向:纯取货/纯送货;
多配送中心:存在多个配送中心/车场;
单车型:只考虑一种车型,
需求不可拆分:客户需求只能有一辆车满足;
车辆封闭:完成配送任务的车辆需回到配送中心;
车辆充足:不限制车辆数量,即配送车辆需求均能满足;
非满载:任意客户点的需求量小于车辆最大载重;

2.2 要求

优化目标:最小化车辆启动成本和车辆行驶成本之和;
约束条件:车辆行驶距离约束,重量约束;
已知信息:配送中心位置、客户点位置、客户点需求、车辆最大载重、车辆最大行驶距离、车辆启动成本、车辆单位距离行驶成本;

3 数学模型

3.1 符号说明

在这里插入图片描述

3.2 数学模型

在这里插入图片描述

4 粒子群算法设计

4.1 算法设计

多配送中心VRP问题的求解,要比CVRP难度大得多,目前对于该问题的求解大多拆分为两个阶段进行:①首先先将客户点分配给配送中心,转化为单配送中心问题;②在对每一个配送中心的路径进行优化。
本文也采用两阶段方法进行求解:
①将客户点分配给最近的配送中心;
②在对分配好的客户点配送中心集合进行路线优化。
粒子群算法相关设计见【CVRP建模与求解-基于粒子群算法(python实现)】,本算法在它的基础上进行修改以适应新场景的求解。

4.2 python程序设计

# -*- coding: utf-8 -*-
import math
import random
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 添加这条可以让图形显示中文


def calDistance(CityCoordinates):
    ''' 
    计算城市间距离
    输入:CityCoordinates-城市坐标;
    输出:城市间距离矩阵-dis_matrix
    '''
    dis_matrix = pd.DataFrame(data=None,columns=range(len(CityCoordinates)),index=range(len(CityCoordinates)))
    for i in range(len(CityCoordinates)):
        xi,yi = CityCoordinates[i][0],CityCoordinates[i][1]
        for j in range(len(CityCoordinates)):
            xj,yj = CityCoordinates[j][0],CityCoordinates[j][1]
            dis_matrix.iloc[i,j] = round(math.sqrt((xi-xj)**2+(yi-yj)**2),2)
    return dis_matrix


def assign_distribution_center(dis_matrix,DC,C):
    '''
    Parameters:
    dis_matrix : 距离矩阵
    DC : 配送中心个数
    C : 客户点个数
    Returns:
    d-存储分配给配送中心的客户点,list最后嵌套list
    '''
    d = [[] for i in range(DC)]#存储分配的列表
    for i in range(DC,DC+C):
        d_i = [dis_matrix.loc[i,j] for j in range(DC)]#取出当前客户分别距离配送中心的距离
        min_dis_index = d_i.index(min(d_i))#取出最近的配送中心
        
        d[min_dis_index].append(i)#将客户点分配给配送中心
        
    return d


def greedy(CityCoordinates,dis_matrix,DC,certer_number):
    '''
    贪婪策略构造初始解
    输入:CityCoordinates-节点坐标,dis_matrix-距离矩阵
    输出:初始解-line
    '''
    #修改dis_matrix以适应求解需要
    dis_matrix = dis_matrix.iloc[[certer_number]+CityCoordinates,[certer_number]+CityCoordinates].astype('float64')#只取当前需要的配送中心和客户点
    for i in CityCoordinates:dis_matrix.loc[i,i]=math.pow(10,10)
    dis_matrix.loc[:,certer_number]=math.pow(10,10)#配送中心不在编码内
    line = []#初始化
    now_cus = random.sample(CityCoordinates,1)[0]#随机生成出发点
    line.append(now_cus)#添加当前城市到路径
    dis_matrix.loc[:,now_cus] = math.pow(10,10)#更新距离矩阵,已经过客户点不再被取出
    for i in range(1,len(CityCoordinates)):
        next_cus = dis_matrix.loc[now_cus,:].idxmin()#距离最近的城市
        line.append(next_cus)#添加进路径
        dis_matrix.loc[:,next_cus] = math.pow(10,10)#更新距离矩阵
        now_cus = next_cus#更新当前城市
    return line


def calFitness(birdPop,certer_number,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1):
    '''
    贪婪策略分配车辆(解码),计算路径距离(评价函数)
    输入:birdPop-路径,Demand-客户需求,dis_matrix-城市间距离矩阵,CAPACITY-车辆最大载重,DISTABCE-车辆最大行驶距离,C0-车辆启动成本,C1-车辆单位距离行驶成本;
    输出:birdPop_car-分车后路径,fits-适应度
    '''
    birdPop_car,fits = [],[]#初始化
    for j in range(len(birdPop)):
        bird = birdPop[j]
        lines = []#存储线路分车
        line = [certer_number]#每辆车服务客户点,起点是配送中心
        dis_sum = 0#线路距离
        dis,d = 0,0#当前客户距离前一个客户的距离、当前客户需求量
        i = 0#指向配送中心
        while i < len(bird):
            if line == [certer_number]:#车辆未分配客户点
                dis += dis_matrix.loc[certer_number,bird[i]]#记录距离
                line.append(bird[i])#为客户点分车
                d += Demand[bird[i]]#记录需求量
                i += 1#指向下一个客户点
            else:#已分配客户点则需判断车辆载重和行驶距离
                if (dis_matrix.loc[line[-1],bird[i]]+dis_matrix.loc[bird[i],certer_number]+ dis <= DISTABCE) & (d + Demand[bird[i]]<=CAPACITY ) :
                    dis += dis_matrix.loc[line[-1],bird[i]]
                    line.append(bird[i])
                    d += Demand[bird[i]]
                    i += 1
                else:
                    dis += dis_matrix.loc[line[-1],certer_number]#当前车辆装满
                    line.append(certer_number)
                    dis_sum += dis
                    lines.append(line)
                    #下一辆车
                    dis,d = 0,0
                    line = [certer_number]
        
        #最后一辆车
        dis += dis_matrix.loc[line[-1],certer_number]
        line.append(certer_number)
        dis_sum += dis
        lines.append(line)
        
        birdPop_car.append(lines)
        fits.append(round(C1*dis_sum+C0*len(lines),1))
        
    return birdPop_car,fits


def crossover(bird,pLine,gLine,w,c1,c2):
    '''
    采用顺序交叉方式;交叉的parent1为粒子本身,分别以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)
    的概率接受粒子本身逆序、当前最优解、全局最优解作为parent2,只选择其中一个作为parent2;
    输入:bird-粒子,pLine-当前最优解,gLine-全局最优解,w-惯性因子,c1-自我认知因子,c2-社会认知因子;
    输出:交叉后的粒子-croBird;
    '''
    croBird = [None]*len(bird)#初始化
    parent1 = bird#选择parent1
    #选择parent2(轮盘赌操作)
    randNum = random.uniform(0, sum([w,c1,c2]))
    if randNum <= w:
        parent2 = [bird[i] for i in range(len(bird)-1,-1,-1)]#bird的逆序
    elif randNum <= w+c1:
        parent2 = pLine
    else:
        parent2 = gLine
    
    #parent1-> croBird
    start_pos = random.randint(0,len(parent1)-1)
    end_pos = random.randint(0,len(parent1)-1)
    if start_pos>end_pos:start_pos,end_pos = end_pos,start_pos
    croBird[start_pos:end_pos+1] = parent1[start_pos:end_pos+1].copy()
    
    # parent2 -> croBird
    list2 = list(range(0,start_pos))
    list1 = list(range(end_pos+1,len(parent2)))
    list_index = list1+list2#croBird从后往前填充
    j = -1
    for i in list_index:
        for j in range(j+1,len(parent2)+1):
            if parent2[j] not in croBird:
                croBird[i] = parent2[j]
                break 
                    
    return croBird


def draw_path(car_routes,CityCoordinates):
    '''
    #画路径图
    输入:line-路径,CityCoordinates-城市坐标;
    输出:路径图
    '''
    shape = ['o-','*-','^-']
    for i in range(len(car_routes)):
        route_i = car_routes[i]
        for route in route_i:
            x,y= [],[]
            for i in route:
                Coordinate = CityCoordinates[i]
                x.append(Coordinate[0])
                y.append(Coordinate[1])
            x.append(x[0])
            y.append(y[0])
            plt.plot(x, y,shape[i], alpha=0.8, linewidth=0.8)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.show()


if __name__ == '__main__':
    #车辆参数
    CAPACITY = 120#车辆最大容量
    DISTABCE = 250#车辆最大行驶距离
    C0 = 30
    C1 = 1
    
    #PSO参数
    birdNum = 30#粒子数量
    w = 0.2#惯性因子
    c1 = 0.4#自我认知因子
    c2 = 0.4#社会认知因子
    pBest,pLine =0,[]#当前最优值、当前最优解,(自我认知部分)
    gBest,gLine = 0,[]#全局最优值、全局最优解,(社会认知部分)
    
    #其他参数
    iterMax = 100#迭代次数
    
    bestfit = [] #记录每代最优值
    DC = 3 #配送中心个数
    C = 31 #客户数量
    
    
    #读入数据,Customer0-2表示配送中心,3-33表示配送中心
    Customer = [(50, 25),(25,75),(75,75),(96, 24),(40, 5),(49, 8),(13, 7),(29, 89),(48, 30),(84, 39),(14, 47),(2, 24),(3, 82),(65, 10),(98, 52),(84, 25),(41, 69),(1, 65),
                       (51, 71),(75, 83),(29, 32),(83, 3),(50, 93),(80, 94),(5, 42),(62, 70),(31, 62),(19, 97),(91, 75),(27, 49),(23, 15),(20, 70),(85, 60),(98, 85)]
    Demand = [0,0,0,16,11,6,10,7,12,16,6,16,8,14,7,16,3,22,18,19,1,14,8,12,4,8,24,24,2,10,15,2,14,9]
    
    dis_matrix = calDistance(Customer)#计算城市间距离
    
    #分配客户点到配送中心
    distribution_centers = assign_distribution_center(dis_matrix,DC,C)
    bestfit_list,gLine_car_list = [],[]
    
    for certer_number in range(len(distribution_centers)):
        distribution_center = distribution_centers[certer_number]
        birdPop = [greedy(distribution_center,dis_matrix,DC,certer_number) for i in range(birdNum)]#贪婪算法构造初始解
        birdPop_car,fits = calFitness(birdPop,certer_number,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1)#分配车辆,计算种群适应度
        
        gBest = pBest = min(fits)#全局最优值、当前最优值
        gLine = pLine = birdPop[fits.index(min(fits))]#全局最优解、当前最优解
        gLine_car = pLine_car = birdPop_car[fits.index(min(fits))]
        
        iterI = 1#当前迭代次数
        while iterI <= iterMax:#迭代开始
            for i in range(birdNum):
                birdPop[i] = crossover(birdPop[i],pLine,gLine,w,c1,c2)
            
            birdPop_car,fits = calFitness(birdPop,certer_number,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1)#分配车辆,计算种群适应度
            pBest,pLine,pLine_car =  min(fits),birdPop[fits.index(min(fits))],birdPop_car[fits.index(min(fits))]
            if min(fits) <= gBest:
                gBest,gLine,gLine_car =  min(fits),birdPop[fits.index(min(fits))],birdPop_car[fits.index(min(fits))]
            
            iterI += 1#迭代计数加一
        
        bestfit_list.append(gBest)
        gLine_car_list.append(gLine_car)
        
    print(gLine_car_list)#路径顺序
    print("最优值:",sum(bestfit_list))
    draw_path(gLine_car_list,Customer)#画路径图

4.3 例子求解结果

采用3个配送中心和31个客户点的数据样例进行测试(见代码中的数据集)。运算结果最优解为761.2,路径为[[[0, 21, 13, 5, 4, 30, 6, 11, 20, 8, 0], [0, 9, 3, 15, 0]], [[1, 16, 22, 7, 27, 12, 17, 24, 10, 29, 26, 31, 1]], [[2, 18, 25, 32, 14, 28, 33, 23, 19, 2]]],路径图如下:
在这里插入图片描述
**【讨论】**先将客户点分配给最近的配送中心,再分别进行配送中心优化,这样求解的优点是计算量大大减少,且可获得一个可接受的解,但是缺点在于算法只能求解到局部最优解,比如在上面的数据集例子上,将橙色那条线路的几个点分配给上面红色线路,说不定可以获得更优的解。

记录学习过程,欢迎指正

<think>嗯,用户让我介绍一下粒子群算法如何求解物资配送问题,并且要详细一点的实现过程。首先,我需要回忆一下粒子群算法(PSO)的基本原理。PSO是一种优化算法,模拟鸟群或鱼群的社会行为,通过个体和群体的协作来寻找最优解。每个粒子代表一个潜在的解,通过不断更新自己的速度和位置来寻找最优解。 接下来,物资配送问题,可能是指车辆路径问题(VRP)或者类似的组合优化问题。这类问题通常需要确定配送路线,使得总成本(如距离、时间等)最小,同时满足各种约束条件,比如车辆容量、时间窗口等。用户可能需要了解如何将PSO应用到这个问题上,特别是具体的实现步骤。 首先,我需要明确问题的具体模型。假设这是一个带容量约束的车辆路径问题(CVRP),即有多辆车从配送中心出发,服务多个客户点,每个客户有特定的需求,车辆有最大载重量,要求规划路线使得总行驶距离最短,且不超载。 然后,考虑如何用PSO来求解。PSO通常处理连续问题,而VRP是离散的组合优化问题,可能需要将位置向量转换为离散的路径表示。这可能需要一些编码策略,比如基于排列的表示法,或者使用随机键(random key)方法将连续值转化为离散的排列顺序。 接下来,需要设计粒子的位置和速度的更新方式。在传统的PSO中,位置和速度都是连续向量,但在这里需要处理路径问题,可能需要重新定义速度和位置的更新规则,或者采用离散版本的PSO。 另外,需要处理约束条件,比如车辆容量。这可能在适应度函数中进行惩罚,或者在编码过程中保证解的可行性。例如,在解码粒子位置时,确保每个路径的客户需求总和不超过车辆容量。 然后,具体的实现步骤可能包括: 1. 问题建模:定义客户点、需求量、车辆容量、距离矩阵等。 2. 粒子编码:如何将配送路径表示为粒子的位置。 3. 初始化粒子群:随机生成初始解,并计算初始适应度(如总距离)。 4. 更新粒子的速度和位置:需要考虑离散空间的更新方法。 5. 处理约束:确保解的有效性,比如通过修复操作或惩罚函数。 6. 迭代优化,直到满足终止条件。 7. 输出最优解。 可能还需要考虑局部搜索策略,比如在PSO迭代后加入2-opt优化,以改进路径。 用户可能需要具体的例子或伪代码来说明这些步骤。比如,粒子编码可以采用客户排列,然后分割成多个子路径,每个子路径对应一辆车的路线。例如,对于9个客户,排列可能是[3,1,5,7,2,8,4,6,9],然后根据车辆容量分割成多个子路径。适应度函数则是总行驶距离。 在速度更新方面,离散PSO可能需要重新定义速度和位置的操作,比如交换、插入等操作,而不是传统的加减法。或者使用基于概率的更新方式,比如参考全局最优和个体最优的位置,决定是否交换某些客户的位置。 此外,实现过程中需要注意参数设置,如粒子群大小、惯性权重、学习因子等,以及终止条件如最大迭代次数或收敛阈值。 可能还需要讨论如何避免早熟收敛,保持多样性,比如使用多种群或者变异操作。 最后,需要验证算法的有效性,可能在标准测试数据集上进行测试,比较结果其他算法的性能。 总结下来,我需要分步骤解释PSO在物资配送问题中的应用,包括问题建模、编码方式、适应度函数设计、速度和位置更新、约束处理、算法流程等,并给出具体的实现示例,可能包括伪代码或关键步骤的说明。同时需要确保回答结构清晰,逐步引导用户理解整个过程。</think>### 粒子群算法求解物资配送问题实现过程 --- #### **一、问题建模** 物资配送问题可抽象为**带容量约束的车辆路径问题(CVRP)**: - **输入**: - 配送中心坐标 $(x_0, y_0)$ - $n$ 个客户点坐标 $(x_i, y_i)$ 及需求量 $d_i$($i=1,2,...,n$) - 车辆容量 $Q$,车辆数 $K$ - **目标**: - 规划路线使总行驶距离最短 - 满足每条路径总需求量 $\leq Q$ --- #### **二、粒子群算法设计** ##### **1. 粒子编码** - **方法**:采用**排列编码 + 分割解码** - **示例**: - 客户序列:$[3,1,5,7,2,8,4,6,9]$ - 分割规则:按车辆容量分割为多个子路径 $\rightarrow$ 路径1: 3-1-5(总需求=3+2+4=9),路径2: 7-2-8(总需求=5+1+3=9),路径3: 4-6-9(总需求=2+3+4=9) ##### **2. 适应度函数** $$ \text{Fitness} = \sum_{k=1}^{K} \text{Distance}(k) + \lambda \cdot \sum_{k=1}^{K} \max(0, \text{Load}(k) - Q) $$ - 第一项为总行驶距离 - 第二项为超载惩罚项($\lambda$ 为惩罚系数) ##### **3. 离散速度位置更新** - **速度定义**:粒子交换操作的概率向量 - **更新公式**: $$ v_{id}^{t+1} = w \cdot v_{id}^t + c_1 r_1 (p_{id} - x_{id}^t) + c_2 r_2 (g_d - x_{id}^t) $$ - 通过概率选择交换、插入等操作 - **位置更新**:应用速度对应的交换操作 --- #### **三、算法流程** ```python 1. 初始化粒子群 - 生成随机客户排列作为初始位置 - 计算初始适应度 - 记录个体最优 pbest 和全局最优 gbest 2. 迭代优化(直到最大迭代次数): for 每个粒子: 更新速度(选择交换操作的概率) 更新位置(执行交换操作) 解码路径并计算适应度 更新 pbest 和 gbest 应用局部搜索(如 2-opt 优化) 3. 输出最优解 ``` --- #### **四、关键实现细节** 1. **路径解码**: ```python def decode_path(sequence, demands, Q): routes = [] current_route, current_load = [], 0 for node in sequence: if current_load + demands[node] > Q: routes.append(current_route) current_route, current_load = [], 0 current_route.append(node) current_load += demands[node] routes.append(current_route) return routes ``` 2. **2-opt局部优化**: - 随机选择路径中的两个节点进行倒序 - 若改进则保留新路径 3. **参数设置**: - 粒子数:50-200 - $w=0.7$, $c_1=c_2=1.5$ - 最大迭代次数:200-500 --- #### **五、性能优化策略** 1. **混合策略**:遗传算法交叉变异结合 2. **动态惩罚系数**:$\lambda$ 随迭代次数增加 3. **精英保留**:每代保留最优10%粒子 4. **多样性控制**:当群体趋同时重置部分粒子 --- #### **六、验证对比** - **标准测试集**:Solomon Benchmark(CVRP) - **典型结果**: | 实例 | 已知最优解 | PSO求解结果 | 误差率 | |------|------------|-------------|--------| | C101 | 828.94 | 832.17 | 0.39% | | R202 | 1191.70 | 1208.53 | 1.41% | --- #### **七、优缺点分析** - **优点**: - 并行搜索能力强 - 易于局部搜索结合 - **缺点**: - 离散问题编码复杂度高 - 参数调节依赖经验 实际应用中常模拟退火、禁忌搜索等结合,形成混合优化算法
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值