【进阶三】Python实现(MD)HFVRPTW常见求解算法——遗传算法(GA)

Python实战开发及案例分析(16)—— 遗传算法 遗传算法(Genetic Algorithm, GA)是一种模拟自然选择和遗传学原理的搜索启发式算法。它们通常用于解决优化和搜索问题,基于“适者生存”的自然选择概念,通过选择、交叉(杂交)、变异操作在一系列迭代中逐步优化解决方案。 阅读详情

基于python语言,实现经典遗传算法(GA)对多车场(Multi-depot)、异构固定车辆(heterogeneous fixed fleet)、带有服务时间窗(time window)等限制约束的车辆路径规划问题((MD)HVRPTW)进行求解。

往期优质资源

1. 适用场景

  • 求解HFVRPTW或MDHFVRPTW
  • 异构固定车队
  • 车辆容量不小于需求节点最大需求
  • 车辆路径长度或运行时间无限制
  • 需求节点服务成本满足三角不等式
  • 节点时间窗至少满足车辆路径只包含一个需求节点的情况
  • 多车辆基地或单一
  • 各车场车辆总数满足实际需求

2. 求解效果

(1)收敛曲线
在这里插入图片描述

(2)车辆路径
在这里插入图片描述

3. 代码分析

本算法继续采用将所有需求节点构造为一个有序列表的编码方式,并运用在交叉、变异等寻优过程中。当需要评估染色体质量时需采用split方法,在考虑车场、异构固定车队、服务时间窗等约束条件下,将有序列表分割为多个可行的车辆路径。split过程也是整个算法的核心,这里复现文末的参考文献中的算法3,并做了适当微调。整个算法的函数调用关系如下图(采用PyCallGraph绘制)。
在这里插入图片描述

4. 数据格式

以csv文件储存数据,其中demand.csv文件记录需求节点数据,共包含需求节点id,需求节点横坐标,需求节点纵坐标,需求量;depot.csv文件记录车场节点数据,共包含车场id,车场横坐标,车场纵坐标,车队类型,车辆容量,车辆速度,车辆数量,车辆固定成本,车辆单位变动成本,车辆最早开始服务时间,车辆最晚结束服务时间。需要注意的是:需求节点id应为整数,从0开始编号,车场节点id任意,但不可与需求节点id重复(建议以 ‘d’+int 形式,便于程序可视化路线)。 可参考github主页相关文件。

5. 分步实现

(1)数据结构
定义Sol()类,Demand()类,Vehicle()类,Model()类,其属性如下表:

  • Sol()类,表示一个可行解
属性描述
node_id_list需求节点id有序排列集合
obj优化目标值
fitness解的适应度
route_list车辆路径集合,对应MDVRPTW的解
timetable_list车辆节点访问时间集合,对应MDVRPTW的解
distance_of_routes总旅行距离
time_of_routes总时间
  • Demand()类,表示一个需求节点
属性描述
id物理节点id,需唯一
x_coord物理节点x坐标
y_coord物理节点y坐标
demand物理节点需求
start_time最早开始服务(被服务)时间
end_time最晚结束服务(被服务)时间
service_time需求节点服务时间
  • Vehicle()类,表示一个车队节点
属性描述
depot_id车辆归属的车场节点节点id,需唯一
x_coord车辆归属车场节点x坐标
y_coord车辆归属车场节点y坐标
type车辆类型
capacity车辆容量
free_speed车辆运营速度
fixed_cost车辆固定成本
variable_cost车辆变动成本
start_time最早开始服务时间
end_time最晚结束服务时间
  • Model()类,存储算法参数
属性描述
best_sol全局最优解,值类型为Sol()
demand_dict需求节点集合(字典),值类型为Demand()
vehicle_dict车队集合(字典),值类型为Vehicle()
vehicle_type_list车队id集合
demand_id_list需求节点id集合
sol_list种群,值类型为Sol()
distance_matrix节点距离矩阵
number_of_demands需求节点数量
opt_type优化目标类型,0:最小旅行距离,1:最小时间成本
pc交叉概率
pm突变概率
n_select优良个体选择数量
popsize种群规模

(2)文件读取

def readCSVFile(demand_file,depot_file,model):
    with open(demand_file,'r') as f:
        demand_reader=csv.DictReader(f)
        for row in demand_reader:
            demand = Demand()
            demand.id = int(row['id'])
            demand.x_coord = float(row['x_coord'])
            demand.y_coord = float(row['y_coord'])
            demand.demand = float(row['demand'])
            demand.start_time=float(row['start_time'])
            demand.end_time=float(row['end_time'])
            demand.service_time=float(row['service_time'])
            model.demand_dict[demand.id] = demand
            model.demand_id_list.append(demand.id)
        model.number_of_demands=len(model.demand_id_list)

    with open(depot_file, 'r') as f:
        depot_reader = csv.DictReader(f)
        for row in depot_reader:
            vehicle = Vehicle()
            vehicle.depot_id = row['depot_id']
            vehicle.x_coord = float(row['x_coord'])
            vehicle.y_coord = float(row['y_coord'])
            vehicle.type = row['vehicle_type']
            vehicle.capacity=float(row['vehicle_capacity'])
            vehicle.free_speed=float(row['vehicle_speed'])
            vehicle.numbers=float(row['number_of_vehicle'])
            vehicle.fixed_cost=float(row['fixed_cost'])
            vehicle.variable_cost=float(row['variable_cost'])
            vehicle.start_time=float(row['start_time'])
            vehicle.end_time=float(row['end_time'])
            model.vehicle_dict[vehicle.type] = vehicle
            model.vehicle_type_list.append(vehicle.type)

(3)计算距离矩阵

def calDistanceMatrix(model):
    for i in range(len(model.demand_id_list)):
        from_node_id = model.demand_id_list[i]
        for j in range(i + 1, len(model.demand_id_list)):
            to_node_id = model.demand_id_list[j]
            dist = math.sqrt((model.demand_dict[from_node_id].x_coord - model.demand_dict[to_node_id].x_coord) ** 2
                             + (model.demand_dict[from_node_id].y_coord - model.demand_dict[to_node_id].y_coord) ** 2)
            model.distance_matrix[from_node_id, to_node_id] = dist
            model.distance_matrix[to_node_id, from_node_id] = dist
        for _, vehicle in model.vehicle_dict.items():
            dist = math.sqrt((model.demand_dict[from_node_id].x_coord - vehicle.x_coord) ** 2
                             + (model.demand_dict[from_node_id].y_coord - vehicle.y_coord) ** 2)
            model.distance_matrix[from_node_id, vehicle.type] = dist
            model.distance_matrix[vehicle.type, from_node_id] = dist

(4)分割路径
split过程采用标号法最短路思想,为了避免在搜索过程中产生大量劣质节点标签,通过一定规则删除劣质标签:根据帕累托删除被支配的标签;根据剩余容量与剩余需求决定是否生成新标签。
在搜索过程中需要计算可能车辆路径的成本(时间成本或距离成本),如果采用时间成本,这里为了简化,只计算旅行距离成本,忽略了等待时间成本。但在计算适应度部分是严格按照时间成本内容计算的(旅行时间成本+等待时间成本)。

"检查路径是否满足时间要求,不满足要求则不会产生新的标签"
def checkTimeWindow(route,model,vehicle):
    timetable=[]
    departure=0
    for i in range(len(route)):
        if i == 0:
            next_node_id = route[i + 1]
            travel_time = int(model.distance_matrix[vehicle.type, next_node_id] /vehicle.free_speed)
            departure = max(0, model.demand_dict[next_node_id].start_time - travel_time)
            timetable.append((int(departure), int(departure)))
        elif 1 <= i <= len(route) - 2:
            last_node_id = route[i - 1]
            current_node_id = route[i]
            current_node = model.demand_dict[current_node_id]
            travel_time = int(model.distance_matrix[last_node_id, current_node_id] / vehicle.free_speed)
            arrival = max(timetable[-1][1] + travel_time, current_node.start_time)
            departure = arrival + current_node.service_time
            timetable.append((int(arrival), int(departure)))
            if departure > current_node.end_time:
                departure = float('inf')
                break
        else:
            last_node_id = route[i - 1]
            travel_time = int(model.distance_matrix[last_node_id, vehicle.type] / vehicle.free_speed)
            departure = timetable[-1][1] + travel_time
            timetable.append((int(departure), int(departure)))
    if departure<vehicle.end_time:
        return True
    else:
        return False

"当产生新的标签W后,检查剩余的车辆容量之和是否能满足剩余未被检车的检点的总需求,如果总容量<总需求,则舍弃W,表明采用W后会导致解不可行"
"这也是减少无效标签的途径之一"
def checkResidualCapacity(residual_node_id_list,W,model):
    residual_fleet_capacity=0
    residual_demand = 0
    for node_id in residual_node_id_list:
        residual_demand+=model.demand_dict[node_id].demand
    for k,v_type in enumerate(model.vehicle_type_list):
        vehicle=model.vehicle_dict[v_type]
        residual_fleet_capacity+=(vehicle.numbers-W[k+4])*vehicle.capacity
    if residual_demand<=residual_fleet_capacity:
        return True
    else:
        return False

"由于标号法会产生大量标签,为了降标签数量,减少对劣质标签的搜索,在插入新标签时根据帕累托,删除支配解"
def updateNodeLabels(label_list,W,number_of_lables):
    new_label_list=[]
    if len(label_list)==0:
        number_of_lables += 1
        W[0] = number_of_lables
        new_label_list.append(W)
    else:
        for label in label_list:
            if W[3]<=label[3] and sum(W[4:])<=sum(label[4:]):
                if W not in new_label_list:
                    number_of_lables += 1
                    W[0] = number_of_lables
                    new_label_list.append(W)
            elif W[3]<=label[3] and sum(W[4:])>sum(label[4:]):
                new_label_list.append(label)
                if W not in new_label_list:
                    number_of_lables += 1
                    W[0] = number_of_lables
                    new_label_list.append(W)
            elif W[3]>label[3] and sum(W[4:])<sum(label[4:]):
                new_label_list.append(label)
                if W not in new_label_list:
                    number_of_lables += 1
                    W[0] = number_of_lables
                    new_label_list.append(W)
            elif W[3]>label[3] and sum(W[4:])>=sum(label[4:]):
                new_label_list.append(label)
    return new_label_list,number_of_lables

"根据标号法的求解结果,从中提取出各车辆路径"
def extractRoutes(V,node_id_list,model):
    route_list = []
    min_obj=float('inf')
    pred_label_id=None
    v_type=None
    # search the min cost label of last node of the node_id_list
    for label in V[model.number_of_demands-1]:
        if label[3]<=min_obj:
            min_obj=label[3]
            pred_label_id=label[1]
            v_type=label[2]
    # generate routes by pred_label_id
    route=[node_id_list[-1]]
    indexs=list(range(0,model.number_of_demands))[::-1]
    start=1
    while pred_label_id!=1:
        for i in indexs[start:]:
            stop=False
            for label in V[i]:
                if label[0]==pred_label_id:
                    stop=True
                    pred_label_id=label[1]
                    start=i
                    v_type_=label[2]
                    break
            if not stop:
                route.insert(0,node_id_list[i])
            else:
                route.insert(0,v_type)
                route.append(v_type)
                route_list.append(route)
                route=[node_id_list[i]]
                v_type=v_type_
    route.insert(0,v_type)
    route.append(v_type)
    route_list.append(route)
    return route_list

"采用标号法对node_id_list进行分割,得到车辆路径"
def splitRoutes(node_id_list,model):
    """
    V: dict,key=id,value=[n1,n2,n3,n4,n5,....]
        id:node_id_list的索引
        n1: 当前标签的生成次序
        n2: 生成当前标签的前一个标签的id
        n3: 当前标签对应的车辆类型
        n4: 当前路径的费用,对应与优化目标,当优化目标为旅行时间时,这里为简化计算只考虑节点间的旅行时间,舍去了等待时间
        n5-: 截止到当前标签,各类型车辆的使用数量
    这里采用先搜索车辆集合再搜索标签集合的方法,与原文是相反的"
        假设有a个标签,n个车需要搜索
            若按照原文的搜索顺序:对于任意一个label,都要判断当前路径对于n个车是否满足时间窗要求,搜索次数=a*n;
            若按照本文的搜索顺序。对于任意一个车辆类型,若路径不满足时间窗要求则不进行标签搜索,因此搜索次数应<a*n
    """
    V={i:[] for i in model.demand_id_list}
    V[-1]=[[0]*(len(model.vehicle_type_list)+4)] # -1表示虚拟车场的索引
    V[-1][0][0]=1 # 虚拟车场的标签id为1
    V[-1][0][1]=1 # 虚拟车场的标签的前向标签也为1
    number_of_lables=1
    for i in range(model.number_of_demands):
        n_1=node_id_list[i]
        j=i
        load=0
        distance={v_type:0 for v_type in model.vehicle_type_list}
        while True:
            n_2=node_id_list[j]
            load=load+model.demand_dict[n_2].demand
            stop = False
            for k,v_type in enumerate(model.vehicle_type_list):
                vehicle=model.vehicle_dict[v_type]
                if i == j:
                    distance[v_type]=model.distance_matrix[v_type,n_1]+model.distance_matrix[n_1,v_type]
                else:
                    n_3=node_id_list[j-1]
                    distance[v_type]=distance[v_type]-model.distance_matrix[n_3,v_type]+model.distance_matrix[n_3,n_2]\
                                     +model.distance_matrix[n_2,v_type]
                route=node_id_list[i:j+1]
                route.insert(0,v_type)
                route.append(v_type)
                if not checkTimeWindow(route,model,vehicle): # 检查时间窗,只有满足时间窗才有可能生成新的标签,否则跳过
                    continue
                for id,label in enumerate(V[i-1]):
                    if load<=vehicle.capacity and label[k+4]<vehicle.numbers:
                        stop=True
                        "计算路径成本,这里计算旅行时间成本时,只考虑节点间的旅行时间,暂不考虑等待时间成本"
                        if model.opt_type==0:
                            cost=vehicle.fixed_cost+distance[v_type]*vehicle.variable_cost
                        else:
                            cost=vehicle.fixed_cost+distance[v_type]/vehicle.free_speed*vehicle.variable_cost
                        "由于label是W的前向标签,因此可以在label的基础上生成W"
                        W=copy.deepcopy(label)
                        "将W的前向标签id设置为label的id"
                        W[1]=V[i-1][id][0]
                        "设置W使用的车辆类型"
                        W[2]=v_type
                        "在label的基础上更新W的cost"
                        W[3]=W[3]+cost
                        "在label的基础上更新使用的车辆数"
                        W[k+4]=W[k+4]+1
                        "检车剩余容量约束,判断是否有可能将W作为当前节点的新的标签"
                        if checkResidualCapacity(node_id_list[j+1:],W,model):
                            "根据帕累托将W插入到当前节点的标签列表中,同时删除被支配标签"
                            label_list,number_of_lables=updateNodeLabels(V[j],W,number_of_lables)
                            V[j]=label_list
            j+=1
            if j>=len(node_id_list) or stop==False:
                break
    if len(V[model.number_of_demands-1])>0:
        route_list=extractRoutes(V, node_id_list, model)
        return route_list
    else:
        print("Failed to split the node id list because of the insufficient capacity")
        return None

(5)适应度计算
对于解的评价可采用旅行时间成本或旅行距离成本,关于某一条车辆路径的成本计算如下:

  • 距离成本=车辆固定成本+旅行距离*变动成本
  • 时间成本=车辆固定成本+(旅行时间+等待时间)*变动成本

这里认为单位距离成本=单位旅行时间成本=单位等待时间成本

"计算解的成本,这里对于时间成本包含了节点间旅行时间以及节点处的等待时间"
def calTravelCost(route_list,model):
    timetable_list=[]
    distance_of_routes=0
    time_of_routes=0
    obj=0
    for route in route_list:
        timetable=[]
        vehicle=model.vehicle_dict[route[0]]
        travel_distance=0
        travel_time=0
        v_type = route[0]
        free_speed=vehicle.free_speed
        fixed_cost=vehicle.fixed_cost
        variable_cost=vehicle.variable_cost
        for i in range(len(route)):
            if i == 0:
                next_node_id=route[i+1]
                travel_time_between_nodes=model.distance_matrix[v_type,next_node_id]/free_speed
                departure=max(0,model.demand_dict[next_node_id].start_time-travel_time_between_nodes)
                timetable.append((int(departure),int(departure)))
            elif 1<= i <= len(route)-2:
                last_node_id=route[i-1]
                current_node_id=route[i]
                current_node = model.demand_dict[current_node_id]
                travel_time_between_nodes=model.distance_matrix[last_node_id,current_node_id]/free_speed
                arrival=max(timetable[-1][1]+travel_time_between_nodes,current_node.start_time)
                departure=arrival+current_node.service_time
                timetable.append((int(arrival),int(departure)))
                travel_distance += model.distance_matrix[last_node_id, current_node_id]
                travel_time += model.distance_matrix[last_node_id, current_node_id]/free_speed+\
                                + max(current_node.start_time - arrival, 0)
            else:
                last_node_id = route[i - 1]
                travel_time_between_nodes = model.distance_matrix[last_node_id,v_type]/free_speed
                departure = timetable[-1][1]+travel_time_between_nodes
                timetable.append((int(departure),int(departure)))
                travel_distance += model.distance_matrix[last_node_id,v_type]
                travel_time += model.distance_matrix[last_node_id,v_type]/free_speed
        distance_of_routes+=travel_distance
        time_of_routes+=travel_time
        if model.opt_type==0:
            obj+=fixed_cost+travel_distance*variable_cost
        else:
            obj += fixed_cost + travel_time *variable_cost
        timetable_list.append(timetable)
    return timetable_list,time_of_routes,distance_of_routes,obj

"计算适应度"
def calFitness(model):
    max_obj=-float('inf')
    best_sol=Sol()
    best_sol.obj=float('inf')
    number_of_split_failures=0
    # calculate travel distance and travel time
    for sol in model.sol_list:
        node_id_list=copy.deepcopy(sol.node_id_list)
        ret=splitRoutes(node_id_list, model)
        if ret is not None:
            sol.route_list=ret
            sol.timetable_list, sol.time_of_routes, sol.distance_of_routes, sol.obj=calTravelCost(sol.route_list,model)
            if sol.obj > max_obj:
                max_obj=sol.obj
            if sol.obj < best_sol.obj:
                best_sol=copy.deepcopy(sol)
        else:
            number_of_split_failures+=1
            sol.obj=None
    # calculate fitness
    for sol in model.sol_list:
        sol.fitness=max_obj-sol.obj if sol.obj is not None else max_obj
    if best_sol.obj<model.best_sol.obj:
        model.best_sol=copy.deepcopy(best_sol)
    if number_of_split_failures>=model.popsize*0.3:
        print("There are {} sols that are unfeasible. Please increase the number of vehicles to obtain better solutions."
              .format(number_of_split_failures))

(6)初始解生成

def generateInitialSol(model):
    demand_id_list=copy.deepcopy(model.demand_id_list)
    for i in range(model.popsize):
        seed=int(random.randint(0,10))
        random.seed(seed)
        random.shuffle(demand_id_list)
        sol=Sol()
        sol.node_id_list=copy.deepcopy(demand_id_list)
        model.sol_list.append(sol)

(7)优良个体选择

def selectSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    for i in range(model.n_select):
        f1_index=random.randint(0,len(sol_list)-1)
        f2_index=random.randint(0,len(sol_list)-1)
        f1_fit=sol_list[f1_index].fitness
        f2_fit=sol_list[f2_index].fitness
        if f1_fit<f2_fit:
            model.sol_list.append(sol_list[f2_index])
        else:
            model.sol_list.append(sol_list[f1_index])

(8)交叉

def crossSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        f1_index = random.randint(0, len(sol_list) - 1)
        f2_index = random.randint(0, len(sol_list) - 1)
        if f1_index!=f2_index:
            f1 = copy.deepcopy(sol_list[f1_index])
            f2 = copy.deepcopy(sol_list[f2_index])
            if random.random() <= model.pc:
                cro1_index=int(random.randint(0,len(model.demand_id_list)-1))
                cro2_index=int(random.randint(cro1_index,len(model.demand_id_list)-1))
                new_c1_f = []
                new_c1_m=f1.node_id_list[cro1_index:cro2_index+1]
                new_c1_b = []
                new_c2_f = []
                new_c2_m=f2.node_id_list[cro1_index:cro2_index+1]
                new_c2_b = []
                for index in range(len(model.demand_id_list)):
                    if len(new_c1_f)<cro1_index:
                        if f2.node_id_list[index] not in new_c1_m:
                            new_c1_f.append(f2.node_id_list[index])
                    else:
                        if f2.node_id_list[index] not in new_c1_m:
                            new_c1_b.append(f2.node_id_list[index])
                for index in range(len(model.demand_id_list)):
                    if len(new_c2_f)<cro1_index:
                        if f1.node_id_list[index] not in new_c2_m:
                            new_c2_f.append(f1.node_id_list[index])
                    else:
                        if f1.node_id_list[index] not in new_c2_m:
                            new_c2_b.append(f1.node_id_list[index])
                new_c1=copy.deepcopy(new_c1_f)
                new_c1.extend(new_c1_m)
                new_c1.extend(new_c1_b)
                f1.nodes_seq=new_c1
                new_c2=copy.deepcopy(new_c2_f)
                new_c2.extend(new_c2_m)
                new_c2.extend(new_c2_b)
                f2.nodes_seq=new_c2
                model.sol_list.append(copy.deepcopy(f1))
                model.sol_list.append(copy.deepcopy(f2))
            else:
                model.sol_list.append(copy.deepcopy(f1))
                model.sol_list.append(copy.deepcopy(f2))
            if len(model.sol_list)>model.popsize:
                break

(9)突变

def muSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        f1_index = int(random.randint(0, len(sol_list) - 1))
        f1 = copy.deepcopy(sol_list[f1_index])
        m1_index=random.randint(0,len(model.demand_id_list)-1)
        m2_index=random.randint(0,len(model.demand_id_list)-1)
        if m1_index!=m2_index:
            if random.random() <= model.pm:
                node1=f1.node_id_list[m1_index]
                f1.node_id_list[m1_index]=f1.node_id_list[m2_index]
                f1.node_id_list[m2_index]=node1
                model.sol_list.append(copy.deepcopy(f1))
            else:
                model.sol_list.append(copy.deepcopy(f1))
            if len(model.sol_list)>model.popsize:
                break

(10)绘制收敛曲线

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()

(11)绘制车辆路线

def plotRoutes(model):
    for route in model.best_sol.route_list:
        x_coord=[model.vehicle_dict[route[0]].x_coord]
        y_coord=[model.vehicle_dict[route[0]].y_coord]
        for node_id in route[1:-1]:
            x_coord.append(model.demand_dict[node_id].x_coord)
            y_coord.append(model.demand_dict[node_id].y_coord)
        x_coord.append(model.vehicle_dict[route[-1]].x_coord)
        y_coord.append(model.vehicle_dict[route[-1]].y_coord)
        plt.grid()
        if route[0]=='v1':
            plt.plot(x_coord,y_coord,marker='o',color='black',linewidth=0.5,markersize=5)
        elif route[0]=='v2':
            plt.plot(x_coord,y_coord,marker='o',color='orange',linewidth=0.5,markersize=5)
        elif route[0]=='v3':
            plt.plot(x_coord,y_coord,marker='o',color='r',linewidth=0.5,markersize=5)
        else:
            plt.plot(x_coord, y_coord, marker='o', color='b', linewidth=0.5, markersize=5)
    plt.xlabel('x_coord')
    plt.ylabel('y_coord')
    plt.show()

(12)输出结果

def outPut(model):
    work=xlsxwriter.Workbook('result.xlsx')
    worksheet=work.add_worksheet()
    worksheet.write(0, 0, 'time_of_routes')
    worksheet.write(0, 1, 'distance_of_routes')
    worksheet.write(0, 2, 'opt_type')
    worksheet.write(0, 3, 'obj')
    worksheet.write(1,0,model.best_sol.time_of_routes)
    worksheet.write(1,1,model.best_sol.distance_of_routes)
    worksheet.write(1,2,model.opt_type)
    worksheet.write(1,3,model.best_sol.obj)
    worksheet.write(2, 0,'vehicleID')
    worksheet.write(2, 1,'depotID')
    worksheet.write(2, 2, 'vehicleType')
    worksheet.write(2, 3,'route')
    worksheet.write(2, 4,'timetable')
    for row,route in enumerate(model.best_sol.route_list):
        worksheet.write(row+3,0,str(row+1))
        depot_id=model.vehicle_dict[route[0]].depot_id
        worksheet.write(row+3,1,depot_id)
        worksheet.write(row+3,2,route[0])
        r=[str(i)for i in route]
        worksheet.write(row+3,3, '-'.join(r))
        r=[str(i)for i in model.best_sol.timetable_list[row]]
        worksheet.write(row+3,4, '-'.join(r))
    work.close()

(13)主函数

def run(demand_file,depot_file,epochs,pc,pm,popsize,n_select,opt_type):
    """
    :param demand_file: demand file path
    :param depot_file: depot file path
    :param epochs: Iterations
    :param pc: Crossover probability
    :param pm: Mutation probability
    :param popsize: Population size
    :param n_select: Number of excellent individuals selected
    :param opt_type: Optimization type:0:Minimize the cost of travel distance;1:Minimize the cost of travel time
    :return:
    """
    model=Model()
    model.pc=pc
    model.pm=pm
    model.popsize=popsize
    model.n_select=n_select
    model.opt_type=opt_type
    readCSVFile(demand_file,depot_file,model)
    calDistanceMatrix(model)
    generateInitialSol(model)
    history_best_obj = []
    best_sol=Sol()
    best_sol.obj=float('inf')
    model.best_sol=best_sol
    start_time=time.time()
    for ep in range(epochs):
        calFitness(model)
        selectSol(model)
        crossSol(model)
        muSol(model)
        history_best_obj.append(model.best_sol.obj)
        print("%s/%s, best obj: %s, runtime: %s" % (ep+1,epochs,model.best_sol.obj,time.time()-start_time))
    plotObj(history_best_obj)
    plotRoutes(model)
    outPut(model)

6. 完整代码

如有错误,欢迎交流。
代码和数据文件获取【私】

参考

  1. Order-first split-second methods for vehicle routing problems: A review
遗传算法 定义+特性+原理+公式+Python示例代码(带详细注释) 遗传算法是一种模拟生物进化过程的优化算法,通过模拟自然选择、交叉和变异等操作,以找到最优解决方案。其简单易懂的特性使其广泛应用于工程优化、金融领域、机器学习等多个领域。遗传算法通过不断迭代,从初始群体中筛选出适应度较高的个体,并通过交叉和变异等操作生成新的个体,最终找到最佳解。其全局搜索特性使其能够避免陷入局部最优解,适用于解决各种复杂的优化问题。 阅读详情

相关推荐

遗传算法求解VRPTW_可运行_附带说明_matlab

【达摩老生出品,必属精品,亲测校正,质量保证】 资源名:遗传算法求解VRPTW_可运行_附带说明_matlab 资源类型:matlab项目全套源码 源码说明: 全部项目源码都是经过测试校正后百分百成功运行的,如果您下载后不能运行可联系我进行指导或者更换。 适合人群:新手及有一定经验的开发人员

【VRP问题】基于遗传算法求解带时间窗车辆路径规划问题(GA-VRPTW)附matlab代码...

作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 ????个人主页:Matlab科研工作室????个人信条:格物致知。⛄ 内容介绍有时间窗的车辆路径问题(Vehicle Routing Problem with Time Windows,VRPTW)因为其有重要的现实意义而备受关注.其时间窗...

m0_60703264的博客 462

基于遗传算法的具有时间窗的车辆路径问题解决方案的Python实现

基于遗传算法的具有时间窗的车辆路径问题解决方案的Python实现

【进阶二】Python实现VRPTW常见求解算法——禁忌搜索(TS)

基于python语言,实现经典禁忌搜索算法(TS)对带有时间窗的车辆路径规划问题(VRPTW )进行求解。

Better.C的博客 4038

【开源~磁悬浮】零代码,纯硬件闭环:从霍尔到H桥的磁悬浮稳定术

本文详细介绍了如何不依赖任何代码,仅通过模拟和数字电路的组合,实现一个完整的磁悬浮闭环控制系统。从霍尔传感器检测位置,到运放电路进行信号调理与模拟PID控制,再到H桥驱动电磁铁执行纠偏,全程使用基础电子元器件搭建。文章深入剖析了系统原理、电路设计、调试技巧与实践经验,展现了纯硬件实现自动控制的独特魅力与挑战。

perl8的博客 622

AI市场风起云涌,ai浏览器是最佳的落地项目,现在ai市场的ai浏览器竞争加剧,得ai浏览器者得天下!

从场景覆盖度来看,AI 浏览器已突破 “网页浏览” 的传统边界,成为连接个人生活、工作、消费的 “超级入口”,这是其他 AI 应用难以比拟的落地优势。在个人生活场景,AI 浏览器可基于用户行为数据主动提供服务 —— 当用户浏览马拉松赛事信息时,浏览器能自动生成 “训练计划 + 装备推荐 + 能量补给方案”,并关联预订链接;在工作场景,Opera Neon 浏览器已实现 “网页内容提取 - 代码生成 - 报告撰写” 的全流程辅助,开发者可直接在浏览器内完成网站开发初稿;

zhang9880000的博客 1086

【改进篇】Python实现VRP常见求解算法——遗传算法(GA)

基于python语言,实现经典遗传算法(GA)对车辆路径规划问题(CVRP)进行求解, 优化代码结构,改进Split函数

Better.C的博客 3684

PCIE Lane Margining Linux 环境配置

Copy all the provided files in folder PYC38 or PYC310 to the created pcie_lmt folder. Index of /ftp/python/3.8.10/Copy below commands to a file and rename it as "scan_pcie.py", the commands are from "685662 PCIe-LMT-Gen4_Gen5-r0p77.pdf" in the lane margi

mysongshine的博客 1439

多智能体进化算法求解带硬时间窗约束的VRP问题(附完整python程序代码+思路详解)

出处:优快云博客专栏:《智能优化算法》。接下来我将从另一个角度详细的讲解带硬时间窗约束的VRP问题如何采用多智能体进化算法编程求解。不标题党,我在文末会附上完整python程序代码的下载链接。

Logintern09的博客 2845

Dify DSL文件分享 Dify讨论群 大模型token资源等(持续收集更新)

基于LLMOps平台-Dify的一站式学习平台。包含不限于:Dify案例、DSL文件分享、模型接入、Dify交流讨论等各类资源分享。

同名公众号:dify实验室 3717

人工智能训练师三级考试备考全攻略|报名条件+真题+补贴申领一站式通关(2026版)

本专栏是人工智能训练师三级考证最完整的备考指南,共77篇文章,覆盖从职业认知、报名实操、AI基础知识、数据标注、模型训练测试部署、考前冲刺到考后进阶的全链路。本导读按模块整理了全部文章链接,帮你快速定位所需内容。人工智能训练师是国家认定的正式职业,证书可在osta.mohrss.gov.cn查询,三级证书可申领最高3120元技能补贴。

白话机器学习 1810

【进阶三】Python实现(MD)HFVRPTW常见求解算法——蚁群算法(ACO)

基于python语言,实现经典蚁群算法(ACO)对多车场(Multi-depot)、异构固定车辆(heterogeneous fixed fleet)、带有服务时间窗(time window)等限制约束的车辆路径规划问题((MD)HFVRPTW)进行求解。

Better.C的博客 2286

13、基因相似性度量的相关性及特征选择研究

本文研究了基因相似性度量和特征选择在生物学中的应用,详细介绍了GO术语相似性度量的多种方法,包括语义度量和路径长度度量,并分析了不同方法在酵母和人类数据集上的表现。研究发现,Avg-sum方法和基于路径长度的度量在多数情况下效果较好。此外,通过晚期辐射毒性预测的案例研究,探讨了特征选择的重要性及可能面临的过拟合和噪声干扰问题,并提出了相应的解决策略。

js777的博客 127

【进阶三】Python实现(MD)HFVRPTW常见求解算法——离散粒子群(DPSO)

基于python语言,实现经典遗传算法(GA)对多车场(Multi-depot)、异构固定车辆(heterogeneous fixed fleet)、带有服务时间窗(time window)等限制约束的车辆路径规划问题((MD)HFVRPTW)进行求解。 目录往期资料1. 适用场景2. 求解效果3. 代码分析4. 数据格式5. 分步实现6. 完整代码参考 往期资料 python实现6种智能算法求解CVRP问题 python实现7种智能算法求解MDVRP问题 python实现7种智能算法求解MDVRPT.

Better.C的博客 1539

CDD数据库文件制作(二)——DTC配置

1.DTC库 2.新建DTC DTC相关介绍:https://blog.youkuaiyun.com/LOVE135149/article/details/122565443 ① 选择DTC数据库 ② 点击新建DTC ③ DTC参数编辑 3.将DTC库中的DTC加载到Variant中 找到Fault Memory的DTC Table,鼠标放在DTC Table区域,右键点击选择Copy … Reference from Available DTCs…,即可将DTC库中的故障码加载至Variant中。 虽然选择Co

LOVE135149的博客 1万+

Vue3 - Element Plus 树形表格组件 el-table 一键全部展开折叠功能,支持动态数据及懒加载点击父级才去请求的场景,Tree树状表格每行带有children的表格全部收起和折叠!

vue3,element-plus,表格Table组件,el-table,怎样一键展开/收起,一键展开或折叠树形表格,如何通过按钮来实现全部展开,不生效,一键折叠或展开表格,表格children嵌套,带有子集,多级表格,懒加载,动态数据,table表格的children通过后端请求过来的怎么一键展开请求数据呢,表格如何一键展开和折叠,怎么使用,教程,树形数据table全部展开和折叠,一键展开或折叠树形表格,点击展开接口获取数据,toggleRowExpansion,树形表格懒加载与全部折叠,深层多级,复杂,

🏆 前端领域优质创作者 6805

2. Python实现VRP常见求解算法——遗传算法(GA)

参考笔记 https://github.com/PariseC/Algorithms_for_solving_VRP 1. 适用场景 求解CVRP 车辆类型单一 车辆容量不小于需求节点最大需求 单一车辆基地 2. 问题分析 CVRP问题的解为一组满足需求节点需求的多个车辆的路径集合。假设某物理网络中共有10个顾客节点,编号为1~10,一个车辆基地,编号为0,在满足车辆容量约束与顾客节点需求约束的条件下,此问题的一个可行解可表示为:[0-1-2-0,0-3-4-5-0,0-6-7-8-0,0-9-10-0

qq_36744449的博客 4371

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

手把手带你解决车辆路径问题(VRP)---0基础入门python----遗传算法

车辆路径,python,遗传算法,适应度函数

非计算机相关专业 1467

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【进阶二】Python实现VRPTW常见求解算法——遗传算法(GA)

基于python语言,实现经典遗传算法(GA)对带有时间窗的车辆路径规划问题(VRPTW)进行求解。

Better.C的博客 9932

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

[大邻域算法](MD)VRPTW常见求解算法--代码解析

文章代码的解析篇

panbaoran913的博客 2687

遗传算法优化VRPTW问题概述

本文介绍了遗传算法在优化VRPTW问题中的应用,并提供了一个基于遗传算法的流程图。遗传算法通过模拟自然选择和遗传机制,能够搜索最优解并优化车辆路径规划,以满足VRPTW问题的约束条件和目标函数。通过实验结果的分析,可以验证遗传算法在优化VRPTW问题上的有效性,并为进一步的研究提供指导。

corn1949的博客 3379

[基因遗传算法]进阶之六:VRP的进阶经典问题的解码如何写

这里是对车辆进行了限制?与VRPTW相比,其结构上的变化,应该只是解码过程的变化. 解码后的route_list结构不变,所以后续的求距离时间成本,和计算适应度方法,都是几乎不改变的. 因此,这里重点讨论 如何对MDVRPTW进行解码.例如:node_id_list=[3,4,2,0,1,9,8,7,6,5],分割为[ [3,4],[2,0,1,9,8],[7,6,5]]splitRoutes(node_id_list,model) :根据车容量限制要求,分割为多个route.各车场车辆总数满足实际需求。

panbaoran913的博客 1702

【进阶六】Python实现SDVRPTW常见求解算法——遗传算法(GA)

基于python语言,采用经典遗传算法(GA)对 带硬时间窗的需求拆分车辆路径规划问题(SDVRP) 进行求解。

Better.C的博客 1547

[基因遗传算法]进阶之五:实践VRPTW

定义解的结构,需求节点的结构,depot的结构,model的属性结构.

panbaoran913的博客 3577

【TWVRP】遗传算法求解带时间窗车辆路径规划问题(目标函数:最低成本 模式:单配送中心 多客户 客户接受货物时间)【含Matlab源码 4481期】

遗传算法求解带时间窗车辆路径规划问题(目标函数:最低成本 模式:单配送中心 多客户 客户接受货物时间) 完整的代码,方可运行;可提供运行操作视频!适合小白!

Matlab912100926的博客 465

HTTP协议

文章目录一、HTTP是什么?二、HTTP工作流程三、HTTP特点四、HTTP请求方法 一、HTTP是什么? 超文本传输协议(HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。 HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。 HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。HTTP的标准制定由万维网协会和互联网工程任务

m0_62069822的博客 604
上一篇: 【进阶二】Python实现VRPTW常见求解算法——差分进化算法(DE)
下一篇: 【进阶三】Python实现(MD)HFVRPTW常见求解算法——禁忌搜索(TS)
Better.C
博客等级 码龄9年 1万+粉丝 72原创
评论 30
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Better.C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值