现在市面上已经存在了很多的VRP、VRPTW问题代码。但是针对EVRPTW的matlab开源代码却比较少。
github上找到了一个比较相近的代码,分享如下,大家可以下载学习:
https://github.com/jiujiuxia/GOC-EVRPTW
由于是短暂性的学习VRP问题,且python基础较差。因此,我将对上面代码中的思路2进行学习,并在此基础上稍作改动。
遗传算法作为一种经典的启发式算法,在这里不作过多讲解。
值得注意的是,遗传算法的使用场景十分广泛,包括在其基础上衍生的差分进化算法。有兴趣的UU可以进行深入研究。
主程序:
data = pd.DataFrame()
result = pd.DataFrame()
distance = pd.read_table('input_distance_time.txt',sep = ',')
pd.DataFrame()
二维数据结构,表示一个表格,类似于电子表格的数据结构。每一列可以用于储存不同的类型值。
使用该功能时,需要导入包:
import pandas as pd
import numpy as np
for c in [0]: for i in range(3):data1_title = str(c) +'_'+str(i)+'.xlsx'result_title = 'result_' + str(c) +'_' +str(i) + '.csv'data1 = pd.read_excel(data1_title)result1 = pd.read_csv(result_title)data = data.append(data1)result = result.append(result1)
代码:
for i in range(3):
print(i)
结果:
0
1
2
所以上面这个代码的目的是遍历目前存在的几个excel表。
包括 “0_0.xlsx”、“0_1.xlsx”、“0_2.xlsx”以及“result_0_0”、“result_0_1”、““result_0_2””
station = []
for i in data.index:if i>1000 and i not in station:station.append(i)
print(station)
选择ID大于1000的station,放在station列表中。
result = pd.Series.to_frame(result)
可以将数组转换为DataFrame格式:
for i in range(len(result)):each = result.iloc[i,0]each = each[1:len(each)-1]each = each.split(',')each = [int(x) for x in each]node.append(each)
print(node)
提取第1列中的点,放入node中
for i in range(len(node)):for j in range(len(node[i])):if node[i][j]>1000:station_used.append(node[i][j])if station_used.count(node[i][j])>1:for s in station:if s not in station_used:station_used[len(station_used)-1] = snode[i][j] = sbreak
循环迭代,保证每个遍历的说station都放在node中。
cus_data = data[data['type']==2]
Node = [0] +list(cus_data.index) + station_used
all_Node = [0] +list(cus_data.index) + station
data为 “0_0.xlsx”、“0_1.xlsx”、“0_2.xlsx”中的数据。
cus_data为type为2的数据
d_1 = pd.merge(final_all_data,distance,how = 'inner',left_on = 'ID', right_on = 'from_node')
d_2 = pd.merge(final_all_data,d_1,how = 'inner',left_on = 'ID',right_on = 'to_node')
d_2 = d_2[['from_node','to_node','distance','spend_tm']] test_data = final_all_data
test_dist = d_2
pd.merge,进行表格之间的拼接。
how: One of ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。inner是取交集,outer取并集。比如left:[‘A’,‘B’,‘C’];right[’'A,‘C’,‘D’];inner取交集的话,left中出现的A会和right中出现的买一个A进行匹配拼接,如果没有是B,在right中没有匹配到,则会丢失。'outer’取并集,出现的A会进行一一匹配,没有同时出现的会将缺失的部分添加缺失值。
n = len(test_data[test_data['type'] == 2]) # 客户点数量
print(n)
m = len(test_data[test_data['type'] == 3]) # 换电站数量
excel中type为2,即为客户点。type为3,则为换电站。
k =999 # 车辆数量
Q = 2.5 # 额定载重量, t
Q_V = 16.0
dis = 120000 # 续航里程, m
costPerKilo = 0.014 # 油价
epu = 0.04 # 早到惩罚成本
lpu = 999999999999 # 晚到惩罚成本
一些参数设定。
其中,晚到惩罚成本很大,即不能晚到。
for i in all_Node:#坐标X[i] = test_data.loc[i,'lng']Y[i] = test_data.loc[i,'lat']#需求量t_w[i] = test_data.loc[i,'pack_total_weight']t_v[i] = test_data.loc[i,'pack_total_volume']#时间窗eh[i] = int(test_data.loc[i,'first_receive_tm'])lh[i] = int(test_data.loc[i,'last_receive_tm'])Distance[i,i] = 0Time[i,i] = 0#服务时间h[i] = 30
h[0] = 0
all_node统计的为type=2下的节点。
#距离和时间
for i in test_dist.index:Distance[test_dist.loc[i,'from_node'],test_dist.loc[i,'to_node']] = test_dist.loc[i,'distance']Time[test_dist.loc[i,'from_node'],test_dist.loc[i,'to_node']] = test_dist.loc[i,'spend_tm']
Node = Node[1:]
计算两个节点之间的距离和驾驶时间。
# return fitnessdef getFit(self):fit = distCost = timeCost = overloadCost = overloadCost_V = fuelCost = fixCost = 0dist = [] # from this to next# calculate distancei = 0while i < len(self.data)-1:dist.append(Distance[self.data[i],self.data[i+1]])i += 1# distance costdistCost = sum(dist)*costPerKilo# time costtimeSpent = 0for i in range(len(self.data)-1):# new carif self.data[i] == CENTER:timeSpent = 0# update time spent on roadtimeSpent += Time[self.data[i],self.data[i+1]]# arrive earlyif timeSpent < eh[self.data[i+1]]:timeCost += ((eh[self.data[i+1]] - timeSpent) * epu)timeSpent = eh[self.data[i+1]]# arrive lateelif timeSpent > lh[self.data[i+1]]:timeCost += ((timeSpent - lh[self.data[i+1]]) * lpu)# update timetimeSpent += h[self.data[i+1]] '''# time costtimeCostTotal = 0for i in range(len(self.data)-1):print(i,self.data)# new carif self.data[i] == CENTER:start_time = eh[self.data[i+1]]-Time[self.data[i],self.data[i+1]]end_time = lh[self.data[i+1]]-Time[self.data[i],self.data[i+1]]print(range(start_time,end_time,10))eachcost = []for j in range(start_time,end_time,10):print(j)p = itimeSpent = jwhile True:print(p)# update time spent on roadtimeSpent += Time[self.data[p],self.data[p+1]]# arrive earlyif timeSpent < eh[self.data[p+1]]:if self.data[p] != CENTER:timeCost += ((eh[self.data[p+1]] - timeSpent) * epu)timeSpent = eh[self.data[p+1]]# arrive lateelif timeSpent > lh[self.data[p+1]]:timeCost += ((timeSpent - lh[self.data[p+1]]) * lpu)# update timetimeSpent += h[self.data[p+1]]p = p+1if self.data[p] == CENTER:breakprint(j,timeCost)eachcost.append(timeCost)timeCostTotal.append(min(eachcost))''' # overload cost and out of fuel costload_w = 0load_v = 0 distAfterCharge = 0for i in range(len(self.data)-1):# charge hereif self.data[i] > 1000:distAfterCharge = 0# at center, re-loadelif self.data[i]== CENTER:load_w = 0load_v = 0distAfterCharge = dist[i]# normalelse:load_w += t_w[self.data[i]]load_v += t_v[self.data[i]]distAfterCharge += dist[i]# update load and out of fuel costoverloadCost += (HUGE * (load_w > Q))overloadCost_V += (HUGE * (load_v > Q_V))fuelCost += (HUGE * (distAfterCharge > dis))car_num = self.data.count(0)-1fixCost = car_num*300fit = distCost + timeCost + overloadCost + fuelCost + fixCost + overloadCost_Vreturn 1.0/float(fit)
计算目标函数下的各个成本。
其他代码为遗传算法下的固定代码。不需要任何的修改。