栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

1. 适用场景2. 求解效果3. 代码分析4. 数据格式5. 分步实现6. 完整代码参考

1. 适用场景 求解MDVRPTW或VRPTW车辆类型单一车辆容量不小于需求节点最大需求车辆路径长度或运行时间无限制需求节点服务成本满足三角不等式节点时间窗至少满足车辆路径只包含一个需求节点的情况多车辆基地或单一各车场车辆总数满足实际需求 2. 求解效果

1 收敛曲线

2 车辆路径

3. 代码分析

应用GA算法求解MDVRPTW时保留了已有代码的架构与思路 为能够求解带有时间窗的 多车场 车辆路径规划问题 这里参考既有文献对路径分割算法进行了改进 splitRoutes 函数 在分割车辆路径时不仅考虑了车辆容量限制 还考虑了节点的时间窗约束 以此使得分割后的路径可行。在此改进下继承了大量原有代码 降低了代码改进量。

4. 数据格式

以csv文件储存数据 其中demand.csv文件记录需求节点数据 共包含需求节点id 需求节点横坐标 需求节点纵坐标 需求量 depot.csv文件记录车场节点数据 共包含车场id 车场横坐标 车场纵坐标 车队数量。需要注意的是 需求节点id应为整数 车场节点id任意 但不可与需求节点id重复。 可参考github主页相关文件。

5. 分步实现

1 数据结构
定义Sol()类 Node()类 Model()类 其属性如下表

Sol()类 表示一个可行解 属性描述node_id_list需求节点id有序排列集合obj优化目标值fitness解的适应度route_list车辆路径集合 对应MDVRPTW的解timetable_list车辆节点访问时间集合 对应MDVRPTW的解cost_of_distance距离成本cost_of_time时间成本 Node()类 表示一个网络节点 属性描述id物理节点id 需唯一x_coord物理节点x坐标y_coord物理节点y坐标demand物理节点需求depot_capacity车辆基地车队规模start_time最早开始服务 被服务 时间end_time最晚结束服务 被服务 时间service_time需求节点服务时间 Model()类 存储算法参数 属性描述best_sol全局最优解 值类型为Sol()demand_dict需求节点集合 字典 值类型为Node()depot_dict车场节点集合 字典 值类型为Node()depot_id_list车场节点id集合demand_id_list需求节点id集合sol_list种群 值类型为Sol()distance_matrix节点距离矩阵time_matrix节点旅行时间矩阵opt_type优化目标类型 0 最小旅行距离 1 最小时间成本vehicle_cap车辆容量vehicle_speed车辆行驶速度 用于计算旅行时间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:
 node Node()
 node.id int(row[ id ])
 node.x_coord float(row[ x_coord ])
 node.y_coord float(row[ y_coord ])
 node.demand float(row[ demand ])
 node.start_time float(row[ start_time ])
 node.end_time float(row[ end_time ])
 node.service_time float(row[ service_time ])
 model.demand_dict[node.id] node
 model.demand_id_list.append(node.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:
 node Node()
 node.id row[ id ]
 node.x_coord float(row[ x_coord ])
 node.y_coord float(row[ y_coord ])
 node.depot_capacity float(row[ capacity ])
 node.start_time float(row[ start_time ])
 node.end_time float(row[ end_time ])
 model.depot_dict[node.id] node
 model.depot_id_list.append(node.id)

3 计算距离 时间矩阵

def calDistanceTimeMatrix(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
 model.time_matrix[from_node_id,to_node_id] math.ceil(dist/model.vehicle_speed)
 model.time_matrix[to_node_id,from_node_id] math.ceil(dist/model.vehicle_speed)
 for _, depot in model.depot_dict.items():
 dist math.sqrt((model.demand_dict[from_node_id].x_coord - depot.x_coord) ** 2
 (model.demand_dict[from_node_id].y_coord - depot.y_coord) ** 2)
 model.distance_matrix[from_node_id, depot.id] dist
 model.distance_matrix[depot.id, from_node_id] dist
 model.time_matrix[from_node_id,depot.id] math.ceil(dist/model.vehicle_speed)
 model.time_matrix[depot.id,from_node_id] math.ceil(dist/model.vehicle_speed)

4 适应度计算
适应度计算依赖 splitRoutes 函数对有序节点序列行解分割得到车辆行驶路线 同时在得到各车辆形式路线后在满足车场车队规模条件下分配最近车场 之后调用 calTravelCost 函数确定车辆访问各路径节点的到达和离开时间点 并计算旅行距离成本和旅行时间成本。

def selectDepot(route,depot_dict,model):
 min_in_out_distance float( inf )
 index None
 for _,depot in depot_dict.items():
 if depot.depot_capacity 0:
 in_out_distance model.distance_matrix[depot.id,route[0]] model.distance_matrix[route[-1],depot.id]
 if in_out_distance min_in_out_distance:
 index depot.id
 min_in_out_distance in_out_distance
 if index is None:
 print( there is no vehicle to dispatch )
 sys.exit(0)
 route.insert(0,index)
 route.append(index)
 depot_dict[index].depot_capacity depot_dict[index].depot_capacity-1
 return route,depot_dict
def calTravelCost(route_list,model):
 timetable_list []
 cost_of_distance 0
 cost_of_time 0
 for route in route_list:
 timetable []
 for i in range(len(route)):
 if i 0:
 depot_id route[i]
 next_node_id route[i 1]
 travel_time model.time_matrix[depot_id,next_node_id]
 departure max(0,model.demand_dict[next_node_id].start_time-travel_time)
 timetable.append((departure,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 model.time_matrix[last_node_id,current_node_id]
 arrival max(timetable[-1][1] travel_time,current_node.start_time)
 departure arrival current_node.service_time
 timetable.append((arrival,departure))
 cost_of_distance cost_of_distance model.distance_matrix[last_node_id,current_node_id]
 cost_of_time model.time_matrix[last_node_id,current_node_id] current_node.service_time
 max(current_node.start_time-timetable[-1][1] travel_time,0)
 else:
 last_node_id route[i - 1]
 depot_id route[i]
 travel_time model.time_matrix[last_node_id,depot_id]
 departure timetable[-1][1] travel_time
 timetable.append((departure,departure))
 cost_of_distance model.distance_matrix[last_node_id,depot_id]
 cost_of_time model.time_matrix[last_node_id,depot_id]
 timetable_list.append(timetable)
 return timetable_list,cost_of_time,cost_of_distance
def extractRoutes(node_id_list,Pred,model):
 depot_dict copy.deepcopy(model.depot_dict)
 route_list []
 route []
 label Pred[node_id_list[0]]
 for node_id in node_id_list:
 if Pred[node_id] label:
 route.append(node_id)
 else:
 route, depot_dict selectDepot(route,depot_dict,model)
 route_list.append(route)
 route [node_id]
 label Pred[node_id]
 route, depot_dict selectDepot(route, depot_dict, model)
 route_list.append(route)
 return route_list
def splitRoutes(node_id_list,model):
 depot model.depot_id_list[0]
 V {id:float( inf ) for id in model.demand_id_list}
 V[depot] 0
 Pred {id:depot for id in model.demand_id_list}
 for i in range(len(node_id_list)):
 n_1 node_id_list[i]
 demand 0
 departure 0
 cost 0
 while True:
 n_2 node_id_list[j]
 demand demand model.demand_dict[n_2].demand
 if n_1 n_2:
 arrival max(model.demand_dict[n_2].start_time,model.depot_dict[depot].start_time model.time_matrix[depot,n_2])
 departure arrival model.demand_dict[n_2].service_time model.time_matrix[n_2,depot]
 if model.opt_type 0:
 cost model.distance_matrix[depot,n_2]*2
 else:
 cost model.time_matrix[depot,n_2]*2
 else:
 n_3 node_id_list[j-1]
 arrival max(departure-model.time_matrix[n_3,depot] model.time_matrix[n_3,n_2],model.demand_dict[n_2].start_time)
 departure arrival model.demand_dict[n_2].service_time model.time_matrix[n_2,depot]
 if model.opt_type 0:
 cost cost-model.distance_matrix[n_3,depot] model.distance_matrix[n_3,n_2] model.distance_matrix[n_2,depot]
 else:
 cost cost-model.time_matrix[n_3,depot] model.time_matrix[n_3,n_2]
 model.demand_dict[n_2].start_time-departure-model.time_matrix[n_3,depot] model.time_matrix[n_3,n_2]
 model.time_matrix[n_2,depot]
 if demand model.vehicle_cap and departure-model.time_matrix[n_2,depot] model.demand_dict[n_2].end_time:
 if departure model.depot_dict[depot].end_time:
 n_4 node_id_list[i-1] if i-1 0 else depot
 if V[n_4] cost V[n_2]:
 V[n_2] V[n_4] cost
 Pred[n_2] i-1
 j j 1
 else:
 break
 if j len(node_id_list):
 break
 route_list extractRoutes(node_id_list,Pred,model)
 return len(route_list),route_list
def calFitness(model):
 max_obj -float( inf )
 best_sol Sol()
 best_sol.obj float( inf )
 # calculate travel distance and travel time
 for sol in model.sol_list:
 node_id_list copy.deepcopy(sol.node_id_list)
 num_vehicle, route_list splitRoutes(node_id_list, model)
 # travel cost
 timetable_list,cost_of_time,cost_of_distance calTravelCost(route_list,model)
 if model.opt_type 0:
 sol.obj cost_of_distance
 else:
 sol.obj cost_of_time
 sol.route_list route_list
 sol.timetable_list timetable_list
 sol.cost_of_distance cost_of_distance
 sol.cost_of_time cost_of_time
 if sol.obj max_obj:
 max_obj sol.obj
 if sol.obj best_sol.obj:
 best_sol copy.deepcopy(sol)
 # calculate fitness
 for sol in model.sol_list:
 sol.fitness max_obj-sol.obj
 if best_sol.obj model.best_sol.obj:
 model.best_sol copy.deepcopy(best_sol)

5 初始解生成

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)

6 优良个体选择

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

7 交叉

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

8 突变

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

9 绘制收敛曲线

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

10 绘制车辆路线

def plotRoutes(model):
 for route in model.best_sol.route_list:
 x_coord [model.depot_dict[route[0]].x_coord]
 y_coord [model.depot_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.depot_dict[route[-1]].x_coord)
 y_coord.append(model.depot_dict[route[-1]].y_coord)
 plt.grid()
 if route[0] d1 :
 plt.plot(x_coord,y_coord,marker o ,color black ,linewidth 0.5,markersize 5)
 elif route[0] d2 :
 plt.plot(x_coord,y_coord,marker o ,color orange ,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()

11 输出结果

def outPut(model):
 work xlsxwriter.Workbook( result.xlsx )
 worksheet work.add_worksheet()
 worksheet.write(0, 0, cost_of_time )
 worksheet.write(0, 1, cost_of_distance )
 worksheet.write(0, 2, opt_type )
 worksheet.write(0, 3, obj )
 worksheet.write(1,0,model.best_sol.cost_of_time)
 worksheet.write(1,1,model.best_sol.cost_of_distance)
 worksheet.write(1,2,model.opt_type)
 worksheet.write(1,3,model.best_sol.obj)
 worksheet.write(2,0, vehicleID )
 worksheet.write(2,1, route )
 worksheet.write(2,2, timetable )
 for row,route in enumerate(model.best_sol.route_list):
 worksheet.write(row 3,0, v str(row 1))
 r [str(i)for i in route]
 worksheet.write(row 3,1, - .join(r))
 r [str(i)for i in model.best_sol.timetable_list[row]]
 worksheet.write(row 3,2, - .join(r))
 work.close()

12 主函数

def run(demand_file,depot_file,epochs,pc,pm,popsize,n_select,v_cap,v_speed,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 v_cap: Vehicle capacity
 :param v_speed: Vehicle free speed
 :param opt_type: Optimization type:0:Minimize the cost of travel distance;1:Minimize the cost of travel time
 :return:
 model Model()
 model.vehicle_cap v_cap
 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)
 calDistanceTimeMatrix(model)
 generateInitialSol(model)
 history_best_obj []
 best_sol Sol()
 best_sol.obj float( inf )
 model.best_sol best_sol
 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 % (ep,epochs,model.best_sol.obj))
 plotObj(history_best_obj)
 plotRoutes(model)
 outPut(model)
6. 完整代码

如有错误 欢迎交流。
代码和数据文件可从github主页免费获取

https://github.com/PariseC/Algorithms_for_solving_VRP

参考 Prins C . A simple and e ective evolutionary algorithm for the vehicle routing problem[J]. Computers operations research, 2004, 31(12):p.1985-2002.Cheng, L., A Genetic Algorithm for The Vehicle Routing Problem with Time Windows. 2005.
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/267997.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号