带容量约束且存在特殊客户集合的车辆路径规划问题——建模,Gurobi与遗传求解

目录

一、问题描述

二、问题特点

三、数学模型

1、参数变量设置

 2、目标:最小成本的车辆路径(总行驶路径距离)

 3、约束

 四、Gurobi求解数学模型

五,遗传算法求解

1、算法流程图 

2、直接上代码,基本都有注释。

3、运行结果

 六、原始数据


一、问题描述

某物流企业有一个车场车场坐标位置是(10, 22)企业的每个车的容量是 110。企业共有 30个客户需要被服务,每个客户最多被一辆车访问一次

存在一个特殊客户的集合: (1, 3, 5, 7, 9, 11, 12, 13, 20, 21, 26, 27, 28, 29, 30),其中:此集合中至少有 10个客户被服务,且子集 (1, 3, 5, 7, 9, 11, 12) 中必须被服务至少有 4个客户; (13, 20, 21, 26, 27, 28, 29, 30) 中必须被服务至少 4个;以上特殊集合 (1, 3, 5, 7, 9, 11, 12, 13, 20, 21, 26, 27, 28, 29, 30) 以外的全部其他客户点,必须被服务(一个车辆访问) 且只被服务一次

假设任意两点之间都有路线相连,路线距离就是其直线连接距离,保留 2 位小数(后续小数直接截断,例如, 如果长度为 12.08731, 使用数据为 12.08)

运输成本定义为全部车辆的总行驶路径距离,求最小成本的车辆路径

二、问题特点

1、只有一个车场(配送中心)

2、存在特殊客户集合,并不是所有的客户都需要被访问

3、使用的车辆数目不限制

4、每个客户最多被一辆车访问一次

5、车辆存在容量限制

三、数学模型

为了在本模型中加入MTZ约束以消除子环路,避免模型出现infeasible,将车场的初始点设置为point 0,增加虚拟点作为车场的终点,设置为point N+1。(在本题中就是增加了点31)

关于消除子环路可参考这篇文章:《运筹学修炼日记:TSP中两种不同消除子环路的方法及callback实现(Python调用Gurobi求解,附以王者荣耀视角解读callback的工作逻辑)》

1、参数变量设置

 2、目标:最小成本的车辆路径(总行驶路径距离)

 3、约束

约束部分可参考文章:《CVRP建模与求解-基于粒子群算法(python实现》,并在此文章基础上针对本文的问题做修改和添加约束。

Tips:

  1. 由于本题中30个点并不是都会被访问,所以在约束中,使用了很多大于等于或小于等于这点对于考虑特殊集合非常重要。
  2. 决策变量之间的关系是为了保证,只有当两个客户点都被访问且该车被使用时,决策变量X才有可能为1。(通常我们在解数学模型的时候会遇到求解不可行或解不正确的情况,很大概率可能是决策变量之间缺乏相互约束的问题

 四、Gurobi求解数学模型

  1. 如何安装下载Gurobi,可参考文章《Gurobi+Python平台的搭建安装》
  2. 如何将利用Gurobi求解数学模型,可参考文章《Gurobi +Python 高效数学规划及建模实例》
  3. 如何看懂Gurobi输出的结果,可参考文章《Gurobi教程-从入门到入土-一篇顶万篇》

话不多说,直接上代码。

import gurobipy as gp
from gurobipy import GRB
import numpy as np
import pandas as pd
import copy

#读取数据
df=pd.read_excel('题目3数据.xlsx')#改成自己的文件名
n=df['customer No'].tolist()#节点集合,0到31共32个节点
customer_demand=df['demand'].tolist()#读取列名为“damand”的一列数据,下同
location_x=df['location x'].tolist()
location_y=df['location y'].tolist()

#构建数据结构
N=len(n)-2#客户点数目 30
M=110#车辆容量限制
K=np.arange(1,8)#车辆数目,按所有点都配送的容量(700)看最多需要7辆车
set=[1, 3, 5, 7, 9, 11, 12, 13, 20, 21, 26, 27, 28, 29, 30]
sub_set1=[1, 3, 5, 7, 9, 11, 12]
sub_set2=[13, 20, 21, 26, 27, 28, 29, 30]
set_left=[2,4,6,8,10,14,15,16,17,18,19,22,23,24,25]

#两个节点的距离
d_ij= {}
for i in n:
    for j in n:
        d_ij[i,j]=np.sqrt(pow(location_x[i]-location_x[j], 2)+pow(location_y[i]-location_y[j], 2))
#某个顾客的需求
m_j={}
for j in n:
    m_j[j]=customer_demand[j]
#车辆k从i行驶到j
X_ijk=[]
for i in n[:-1]:
    for j in n[1:]:
        for k in K:
             X_ijk.append((i, j, k))
X_ijk = gp.tuplelist(X_ijk)
#客户点是否被服务
Y_i=copy.deepcopy(n)#0-31
Y_i = gp.tuplelist(Y_i)
#车辆是否被使用
A_k=copy.deepcopy(K)
A_k = gp.tuplelist(A_k)
#消除子路径
u_i=copy.deepcopy(n)#0-31
u_i = gp.tuplelist(u_i)
#模型建立
Model=gp.Model("VRP_gurobi")


#定义变量
X_ijk=Model.addVars(X_ijk,vtype=GRB.BINARY,name="X_ijk")
Y_i=Model.addVars(Y_i,vtype=GRB.BINARY,name="Y_i")
A_k=Model.addVars(A_k,vtype=GRB.BINARY,name="A_k")
u_i=Model.addVars(u_i,vtype=GRB.INTEGER,name="u_i",lb=0)
#更新变量
Model.update()
#目标函数
Model.setObjective(sum(d_ij[i,j] * X_ijk.sum(i,j,"*") for i in n[:-1] for j in n[1:] ),gp.GRB.MINIMIZE)
#客户点服务约束
Model.addConstr(sum(Y_i[i] for i in set )>=10)
Model.addConstr(sum(Y_i[i] for i in sub_set1 )>=4)
Model.addConstr(sum(Y_i[i] for i in sub_set2 )>=4)
Model.addConstr(sum(Y_i[i] for i in set_left )==15)

#配送中心约束:所有车辆均由配送中心出发,完成所有配送任务后返回配送中心
Model.addConstr(sum(X_ijk[(0,j,k)] for j in n[1:] for k in K)==sum(X_ijk[(i,n[-1],k)] for i in n[:-1] for k in K ))
Model.addConstr(sum(X_ijk[(0,j,k)] for j in n[1:] for k in K)== sum(A_k[k] for k in K))
Model.addConstr(sum(X_ijk[(i,n[-1],k)] for i in n[:-1] for k in K )==sum(A_k[k] for k in K))

#车场约束,每辆车每次只能访问一个点
for k in K:
    Model.addConstr(sum(X_ijk[(0,j,k)] for j in n[1:])==sum(X_ijk[(i,n[-1],k)] for i in n[:-1]))
    Model.addConstr(sum(X_ijk[(0,j,k)] for j in n[1:])<=1)
    Model.addConstr(sum(X_ijk[(i,n[-1],k)] for i in n[:-1])<=1)

#客户点服务约束,每个客户只能被服务一次
for j in n[1:-1]:#对于每个客户点来说
    Model.addConstr(sum(X_ijk[i,j,k]for i in n[:-1] for k in K )<=1)
    Model.addConstr(sum(X_ijk[j,i,k] for i in n[1:] for k in K) <= 1)
    Model.addConstr(sum(X_ijk[j, i, k] for i in n[1:] for k in K) == sum(X_ijk[i,j,k]for i in n[:-1] for k in K ))

#客户点流量平衡,进出的车辆数相等
for j in n[1:-1]:
    for k in K:
        Model.addConstr(sum(X_ijk[i, j, k] for i in n[:-1]) == sum(X_ijk[j, i, k] for i in n[1:]))
        Model.addConstr(sum(X_ijk[i, j, k] for i in n[:-1]) <=1)
        Model.addConstr(sum(X_ijk[i, j, k] for i in n[:-1]) <=1)


#消除子路径
for i in n[:-1]:
    for j in n[1:]:
        for k in K:
            Model.addConstr(u_i[i]-u_i[j]+N*X_ijk[i,j,k]<=N-1)
            Model.addConstr(u_i[i]>=0)
            Model.addConstr(u_i[j]>= 0)

#重量约束,每辆车的装载量不能超过最大载重量限制
for k in K:
    Model.addConstr(sum((sum(X_ijk[(i,j,k)] for i in n[:-1]) * m_j[j] )for j in n[1:])<=M)

#所需车辆的约束
Model.addConstr(sum(A_k[k] for k in K) >= sum(m_j[j]*Y_i[j] for j in n[1:])/M)

#决策变量约束
for i in n[:-1]:
    for j in n[1:]:
        for k in K:
            if i==n[0] and j==n[-1] :
                Model.addConstr(X_ijk[(i, j, k)] == 0)
            elif i==j:
                Model.addConstr(X_ijk[(i, j, k)] == 0)
            else:
                Model.addConstr(X_ijk[i, j, k] <= Y_i[i])
                Model.addConstr(X_ijk[i, j, k] <= Y_i[j])
                Model.addConstr(X_ijk[i, j, k] <= A_k[k])
for i in n[1:-1]:
    Model.addConstr(sum(X_ijk[(i, j, k)] for j in n[1:] for k in K) == Y_i[i])
    Model.addConstr(sum(X_ijk[(j, i, k)] for j in n[:-1] for k in K) == Y_i[i])

for k in K:
    Model.addConstr(sum(X_ijk[(i, j, k)] for i in n[:-1] for j in n[1:]) >=A_k[k])


Model.Params.LogToConsole = True#显示求解过程
Model.Params.TimeLimit = 7200
Model.Params.MIPFocus=3#1侧重快速找到最优解 2侧重证明最优 3侧重界的提升
Model.Params.Heuristics=0.2
# 显示冲突的约束(当模型infeasible时可用这段代码查看冲突约束)
# Model.computeIIS()
# Model.write('model.ilp')
Model.optimize()
print("Optimal Objective Value", Model.objVal)

# 查看所有变量取值
for var in Model.getVars():
    if var.X==1:
        print(f"{var.varName}: {round(var.X, 3)}")

五,遗传算法求解

1、算法流程图 

2、直接上代码,基本都有注释。

import copy
import itertools
import math
import random
import time
import numpy as np
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 choosetwentyfive(setall,set_sub,sub_set1,sub_set2):#选择25个点
    new_25 = copy.deepcopy(setall)
    set_sub=set(set_sub)
    set_1 = set(random.sample(sub_set1, 4))
    set_2 = set(random.sample(sub_set2, 4))
    set_union = set_1.union(set_2)
    set_left = set_sub - set_union
    set_3 = set(random.sample(list(set_left), 2))
    for i in (set_left - set_3):
        new_25.remove(i)
    return new_25

def greedy(new_25,Customer, dis_matrix):
    '''
    贪婪策略构造初始解,初始化时将VRP简化为TSP进行构造。
    输入:CityCoordinates-节点坐标,dis_matrix-距离矩阵
    输出:初始解-line
    '''
    # 修改dis_matrix以适应求解需要
    dis_matrix = dis_matrix.astype('float64')
    for i in range(len(Customer)): dis_matrix.loc[i, i] = math.pow(10, 10)
    dis_matrix.loc[:, 0] = math.pow(10, 10)  # 0不在编码内
    birdPop=[]
    for i in range(len(new_25)):
        new_matrix=copy.deepcopy(dis_matrix)
        line = []  # 初始化
        now_city = random.choice(new_25[i])  # 随机生成出发城市
        line.append(now_city)  # 添加当前城市到路径
        new_matrix.loc[:, now_city] = math.pow(10, 10)  # 更新距离矩阵,已经过城市不再被取出
        left_5=list(set(Customer)-set(new_25[i]))
        for m in left_5:#将不能走的五个点设置为无穷大
            new_matrix.loc[:, m] = math.pow(10, 10)
            new_matrix.loc[m, :] = math.pow(10, 10)
        for j in range(len(new_25[i])-1):
            next_city = new_matrix.loc[now_city, :].idxmin()  # 距离最近的城市
            line.append(next_city)  # 添加进路径
            new_matrix.loc[:, next_city] = math.pow(10, 10)  # 更新距离矩阵
            now_city = next_city  # 更新当前城市
        birdPop.append(line)
    return birdPop

def calFitness(birdPop, 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 = [0]  # 每辆车服务客户点
        dis_sum = 0  # 线路距离
        dis, d = 0, 0  # 当前客户距离前一个客户的距离、当前客户需求量
        i = 0  # 指向配送中心
        while i < len(bird):
            if line == [0]:  # 车辆未分配客户点
                dis += dis_matrix.loc[0, 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], 0] + 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], 0]  # 当前车辆装满
                    line.append(0)
                    dis_sum += dis
                    lines.append(line)
                    # 下一辆车
                    dis, d = 0, 0
                    line = [0]
        # 最后一辆车
        dis += dis_matrix.loc[line[-1], 0]
        # line.append(0)
        dis_sum += dis
        lines.append(line)
        #返回参数
        birdPop_car.append(lines)
        fits.append(round(C1 * dis_sum + C0 * len(lines), 2))
    return birdPop_car, fits

def Fitness(bird, Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1):
    birdPop_car, fits = [], []  # 初始化
    lines = []  # 存储线路分车
    line = [0]  # 每辆车服务客户点
    dis_sum = 0  # 线路距离
    dis, d = 0, 0  # 当前客户距离前一个客户的距离、当前客户需求量
    i = 0  # 指向配送中心
    while i < len(bird):
        if line == [0]:  # 车辆未分配客户点
            dis += dis_matrix.loc[0, 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], 0] + 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], 0]  # 当前车辆装满
                line.append(0)
                dis_sum += dis
                lines.append(line)
                # 下一辆车
                dis, d = 0, 0
                line = [0]
    # 最后一辆车
    dis += dis_matrix.loc[line[-1], 0]
    # line.append(0)
    dis_sum += dis
    lines.append(line)
    #返回参数
    birdPop_car=lines
    fits=round(C1 * dis_sum + C0 * len(lines), 2)
    return birdPop_car, fits

def crossover(bird, pLine, gLine, w, c1, c2, c3,Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1):
    randNum = random.uniform(0, sum([w, c1, c2,c3]))
    if randNum <= w:
        new_cro=order_cross(bird,pLine)
    elif randNum <= w + c1:
        new_cro = order_cross(bird, gLine)
    elif randNum <= w + c1+c2:
        list_all = list(np.arange(1, 31))
        choice_bird = list(random.sample(list_all, len(bird)))
        new_cro = order_cross(bird, choice_bird)
    else:
        removed,bird1=max3Destroy(bird,dis_matrix)
        new_cro=greedyInsert(bird1,removed,Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1)
    return new_cro

def order_cross(parent1,parent2):#顺序交叉
    croBird = [None] * len(parent1)  # 初始化
    # 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(parent1)))
    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 max3Destroy(parent1,dis_matrix):#移除最大的三段距离
    '''
    len(disSort)-i-1得到排在最后面即距离最长的路段。len-1,len-2,...
    disSort[]得到最大值,dis.index得到最大值在距离列表中的索引
    solNew得到最大值路段所在起点的索引,+1删除路段终点
    如solNew = 【1,3,2】,dis = 【d13,d32,d21】=【9,7,8】
    disSort = 【7,8,9】,先移除最后面的9,再移除8,...
    9对应的dis索引为0,solnew对应索引0上的城市为1,移除1-3段,去掉3
    '''
    solNew = copy.deepcopy(parent1)
    removed = []
    dis = []
    for i in range(len(parent1) - 1):
        dis.append(dis_matrix.loc[parent1[i],parent1[i + 1]])      #在距离矩阵中选取路段的长度,如【1,2,3】,循环两次求1-2,2-3的距离,
    dis.append(dis_matrix.loc[parent1[-1],parent1[0]])      #选取首尾的距离3-1,放入dis列表

    disdict = {}  # 构造一个值和索引一一对应的字典,方便在排序后依然获得的是初始的index
    for a in range(len(dis)):
        disdict[a] = dis[a]
    fsort = dict(sorted(disdict.items(), key=lambda x: x[1], reverse=True))  # 排序
    sort_index = list(fsort.keys())#最大三个的原始索引

    for i in range(3):     #判断最长的3个路段并移除
        if  sort_index[i]== len(dis) - 1:
            #如果是距离列表的最后一个,就是城市首尾相连的距离,则去掉列表第一个城市
            removed.append(solNew[0])
            parent1.remove(solNew[0])
        else:
            remove_one=solNew[sort_index[i]+1]
            removed.append(remove_one)
            parent1.remove(remove_one)   #更新移除后的城市列表
    return removed,parent1

def greedyInsert(parent1, removed,Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1):
    dis = float('inf')  #初始化距离
    insertIndex = -1   #初始化插入索引
    for i in range(len(removed)):   #移除列表里有几个城市就循环几次
        for j in range(len(parent1) + 1):   #对于可行解中的每一个索引位置
            solNew = copy.deepcopy(parent1)
            solNew.insert(j, removed[i])   #将移除列表中的元素插入新解列表索引j的位置中,生成新的城市访问顺序
            ditance=Fitness(solNew,Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1)[1]
            if ditance < dis:   #插入后得到新解的距离是否<原解距离。solNew和当前解sol不一样,solNew只是作为中间过程插入哪里的判断条件
                dis = ditance   #更新距离,将原解换成新解
                insertIndex = j    #确定移除列表中每一个城市插入的索引
        parent1.insert(insertIndex, removed[i])    #完成每一个城市的插入操作后,更新当前解
        dis = float('inf')   #完成每一个城市的插入操作后距离重新初始化
    return parent1

def draw_path(car_routes, CityCoordinates):
    '''
    #画路径图
    输入:line-路径,CityCoordinates-城市坐标;
    输出:路径图
    '''
    for route in car_routes:
        x, y = [], []
        for i in route:
            Coordinate = CityCoordinates[i]
            x.append(Coordinate[0])
            y.append(Coordinate[1])
        plt.plot(x, y, 'o-', alpha=0.8, linewidth=0.8)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.show()
#绘制收敛曲线
def draw_convergence(bestfit,iterI):
    plt.figure()
    X_labels=[]
    for i in range(1,iterI+1):
        X_labels.append(i)
    plt.plot(X_labels, bestfit, color='black', marker='', linestyle="-")
    # plt.xticks(X_labels,X_labels,rotation=0)
    plt.legend()
    plt.xlabel("iterations")
    plt.ylabel("distance")
    plt.show()

old_time = time.time()#记录运行开始时间
if __name__ == '__main__':
    # 车辆参数
    CAPACITY = 110  # 车辆最大容量
    DISTABCE = 99999  # 车辆最大行驶距离
    C0 = 0  # 车辆启动成本
    C1 = 1  # 车辆单位距离行驶成本

    # PSO参数
    birdNum = 100  # 粒子数量
    w = 0.25  # pline
    c1 = 0.25  # gline
    c2 = 0.15  # random
    c3= 0.35 #destroy_restore
    pBest, pLine = 0, []  # 当前最优值、当前最优解,(自我认知部分)
    gBest, gLine = 0, []  # 全局最优值、全局最优解,(社会认知部分)

    # 其他参数
    iterMax = 1000  # 迭代次数
    same_limit=iterMax*0.6
    iterI = 1  # 当前迭代次数
    bestfit = []  # 记录每代最优值
    #特殊集合
    set_sub = [1, 3, 5, 7, 9, 11, 12, 13, 20, 21, 26, 27, 28, 29, 30]
    sub_set1 = [1, 3, 5, 7, 9, 11, 12]
    sub_set2 = [13, 20, 21, 26, 27, 28, 29, 30]

    df = pd.read_excel('题目3数据.xlsx')
    Customer=df['customer No'].tolist()
    Demand = df['demand'].tolist()
    location_x = df['location x'].tolist()
    location_y = df['location y'].tolist()
    Customer_axes=[]
    for i in range(len(location_x)):
        Customer_axes.append((location_x[i],location_y[i]))
    dis_matrix = calDistance(Customer_axes)  # 计算城市间距离

    #选择25个点
    new_25=[choosetwentyfive(Customer, set_sub,sub_set1,sub_set2) for i in range(birdNum)]
    #对这25个点的组进行贪婪的初始解
    birdPop = greedy(new_25, Customer,dis_matrix)
    # 分配车辆,计算种群适应度
    birdPop_car, fits = calFitness(birdPop, 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))]
    bestfit.append(gBest)
    # 迭代开始
    same_num=0
    while iterI <= iterMax and same_num <= same_limit:
        same_num = max([len(list(v)) for k, v in itertools.groupby(bestfit)])  # 连续generation_num*same_num代的最优值不发生变化
        for i in range(birdNum):
            birdPop[i]= crossover(birdPop[i], pLine, gLine, w, c1, c2, c3,Demand, dis_matrix, CAPACITY, DISTABCE, C0, C1)#交叉
            # new_ran=Random_bird(new_cro,w, c1, c2)#变异
        birdPop_car, fits= calFitness(birdPop, 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))]
        bestfit.append(gBest)
        print(iterI, gBest)  # 打印当前代数和最佳适应度值
        iterI += 1  # 迭代计数加一

    print(gLine_car)  # 路径顺序
    draw_path(gLine_car, Customer_axes)  # 画路径图
    draw_convergence(bestfit,iterI) #画收敛曲线图

current_time = time.time()
print("运行时间为" + str(current_time - old_time) + "s")

3、运行结果

车1:[0, 9, 14, 5, 16, 25, 4, 0],
车2:[0, 12, 3, 23, 22, 11, 0],
车3:[0, 24, 8, 18, 19, 7, 6, 0],
车4:[0, 10, 28, 1, 29, 0],
车5:[0, 2, 13, 15, 27, 0]
总成本:731.74

 六、原始数据

customer Nodemandlocation xlocation y
001022
1101010
2101530
3104577
4102050
5106050
6106015
7109025
8109079
9105418
10102110
11206391
12201671
13202629
14205839
15203315
16203355
17208090
182010079
193010059
20302279
21303381
22304888
23305077
24309076
25402055
26408060
2740305
28402010
2950030
305010080
3101022

备注:gurobi中需要添加31号点,遗传中不需要。

欢迎大家一起交流,指出我算法中值得改进和提高的地方,也希望本篇文章能给大家提供解决问题的思路。 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值