一、VRP和TSP的区别
VRP(Vehicle Routing Problem)问题和TSP(Traveling Salesman Problem)问题都是优化问题,但它们在问题设置和目标函数上存在一些区别。
TSP问题是经典的组合优化问题,其目标是找到一条最短路径,使得一名推销员从一个城市出发,经过所有其他城市后回到起点城市,同时每个城市只能访问一次。TSP问题的目标是最小化总的旅行距离或旅行时间。
VRP问题是在TSP问题的基础上进行扩展的一类问题。VRP问题考虑的是在给定一组客户需求和一组配送车辆的情况下,如何安排车辆的路径,以便满足所有客户的需求,并且使得总的配送成本最小化。VRP问题通常包括以下要素:
- 客户需求:每个客户对应一个需求量,表示需要配送的货物数量或服务时间。
- 车辆容量:每辆车有一个容量限制,表示车辆能够携带的货物数量或服务时间的上限。
- 距离或时间矩阵:表示各个客户之间的距离或行驶时间。
- 路径限制:每辆车的路径应满足起始点和终点相同的条件。
在VRP问题中,目标是找到一组车辆的路径,使得满足客户需求的同时,总的配送成本最小化。这个成本可以是行驶距离、行驶时间、车辆数量等。
因此,可以说VRP问题是TSP问题的一种扩展形式,考虑了更多实际配送问题的约束和要素。在VRP问题中,需要解决的是如何合理分配车辆、安排路径以及满足客户需求的问题,而不仅仅是寻找最短路径。
二、VRPTW介绍
VRPTW(Vehicle Routing Problem with Time Windows)
在VRPTW中,有一组客户需要被服务,这些客户位于不同的位置,每个客户有特定的需求量和时间窗口。时间窗口定义了每个客户允许被访问的时间范围,即服务必须在时间窗口内完成。此外,还有一个或多个车辆可用于服务客户,并且每辆车有容量限制。
VRPTW的目标是找到一组最优路径,使得所有客户的需求得到满足,并且最小化总的路程和车辆的数量。解决VRPTW问题可以帮助优化物流和配送等领域的路线规划,提高效率和降低成本。
三、数学模型
参数:
决策变量:
目标函数:
约束条件:
- 每个乘客必须且只能访问一次
- 每个车辆的路径必须从仓库出发并返回仓库
- 车辆容量限制
- 时间窗口约束
其中M是一个足够大的正数。
这个数学模型描述了VRPTW问题的基本约束和目标,您可以根据具体情况进行进一步的修改和扩展。
四、CPLEX求解
import cplex
from cplex.exceptions import CplexError
# 读取数据
def read_data(filename):
# 从文件中读取服务点、客户的位置
data = {}
node_list = []
with open(filename, 'rt') as f:
count = 1
for line in f:
if count == 5:
vehicle_num, vehicle_capacity = line.split()
vehicle_num = int(vehicle_num)
vehicle_capacity = int(vehicle_capacity)
elif count >= 10:
node_list.append(line.split())
count += 1
for item in node_list:
customer_id = int(item[0])
x = float(item[1])
y = float(item[2])
demand = float(item[3])
ready_time = float(item[4])
due_date = float(item[5])
service_time = float(item[6])
data[customer_id] = {
'x': x,
'y': y,
'demand': demand,
'ready_time': ready_time,
'due_date': due_date,
'service_time': service_time
}
return data, vehicle_num, vehicle_capacity
# 构建VRPTW模型
def build_model(data, num_vehicles, vehicle_capacity):
model = cplex.Cplex()
model.objective.set_sense(model.objective.sense.minimize)
# 创建决策变量
x_vars = {}
for i in data:
for j in data:
for k in range(1, num_vehicles+1):
x_vars[(i, j, k)] = model.variables.get_num()
model.variables.add(obj=[data[i]['x']-data[j]['x'], data[i]['y']-data[j]['y']], lb=[0.0], ub=[1.0], types=['B'])
t_vars = {}
for i in data:
t_vars[i] = model.variables.get_num()
model.variables.add(obj=[0.0], lb=[data[i]['ready_time']], ub=[data[i]['due_date']], types=['C'])
# 添加约束条件
# 每个客户节点必须且仅能被访问一次
for i in data:
model.linear_constraints.add(lin_expr=[[x_vars[(i, j, k)] for j in data] + [x_vars[(j, i, k)] for j in data]], senses=['E'], rhs=[1.0])
# 每个车辆的路径必须从仓库出发并返回仓库
for k in range(1, num_vehicles+1):
model.linear_constraints.add(lin_expr=[[x_vars[(0, j, k)] for j in data]], senses=['E'], rhs=[1.0])
model.linear_constraints.add(lin_expr=[[x_vars[(i, 0, k)] for i in data]], senses=['E'], rhs=[1.0])
# 车辆容量限制
for j in data:
for k in range(1, num_vehicles+1):
model.linear_constraints.add(lin_expr=[[x_vars[(i, j, k)] for i in data]], senses=['L'], rhs=[data[j]['demand']])
# 时间窗口约束
M = max(data[j]['due_date'] for j in data) + max(data[j]['service_time'] for j in data)
for i in data:
for j in data:
for k in range(1, num_vehicles+1):
model.linear_constraints.add(lin_expr=[[t_vars[i], data[i]['service_time'], data[i]['demand'], -M, x_vars[(i, j, k)], t_vars[j]]], senses=['L'], rhs=[data[j]['due_date']])
return model
# 解决VRPTW模型
def solve_model(model, data, num_vehicles):
try:
model.solve()
solution = model.solution
if solution.get_status() == solution.status.optimal:
print('最优解已找到')
print('目标函数值:', solution.get_objective_value())
print('路径安排:')
for k in range(1, num_vehicles+1):
print('车辆', k, ':')
for i in data:
for j in data:
if solution.get_values(x_vars[(i, j, k)]) > 0.9:
print(i, '->', j)
print()
else:
print('求解失败')
except CplexError as e:
print('求解失败:', e)
# 主函数
if __name__ == '__main__':
data, num_vehicles, vehicle_capacity = read_data('C101.txt')
model = build_model(data, num_vehicles, vehicle_capacity)
solve_model(model, data, num_vehicles)
五、蚁群算法求解
import numpy as np
import random
# 读取数据
def read_data(filename):
# 从文件中读取服务点、客户的位置
data = {}
node_list = []
with open(filename, 'rt') as f:
count = 1
for line in f:
if count == 5:
vehicle_num, vehicle_capacity = line.split()
vehicle_num = int(vehicle_num)
vehicle_capacity = int(vehicle_capacity)
elif count >= 10:
node_list.append(line.split())
count += 1
for item in node_list:
customer_id = int(item[0])
x = float(item[1])
y = float(item[2])
demand = float(item[3])
ready_time = float(item[4])
due_date = float(item[5])
service_time = float(item[6])
data[customer_id] = {
'x': x,
'y': y,
'demand': demand,
'ready_time': ready_time,
'due_date': due_date,
'service_time': service_time
}
return data, vehicle_num, vehicle_capacity
# 计算两个节点之间的距离
def calculate_distance(node1, node2):
return np.sqrt((node1['x'] - node2['x'])**2 + (node1['y'] - node2['y'])**2)
# 计算节点间的信息素初始值
def initialize_pheromone(data):
num_nodes = len(data)
pheromone = np.ones((num_nodes, num_nodes))
return pheromone
# 更新信息素
def update_pheromone(pheromone, ants, best_ant, evaporation_rate, Q):
pheromone *= (1 - evaporation_rate) # 蒸发信息素
for ant in ants:
for i in range(len(ant['path']) - 1):
node1 = ant['path'][i]
node2 = ant['path'][i+1]
pheromone[node1][node2] += Q / ant['total_distance'] # 更新路径上的信息素
best_path = best_ant['path']
for i in range(len(best_path) - 1):
node1 = best_path[i]
node2 = best_path[i+1]
pheromone[node1][node2] += Q / best_ant['total_distance'] # 更新最佳路径上的信息素
return pheromone
# 选择下一个节点
def select_next_node(pheromone, current_node, available_nodes, alpha, beta):
probabilities = []
total = 0
for node in available_nodes:
pheromone_value = pheromone[current_node][node]
distance_value = 1 / calculate_distance(data[current_node], data[node])
probability = (pheromone_value**alpha) * (distance_value**beta)
probabilities.append(probability)
total += probability
probabilities = [p / total for p in probabilities]
next_node = random.choices(available_nodes, probabilities)[0]
return next_node
# 构建初始路径
def construct_initial_path(pheromone, alpha, beta):
num_nodes = len(data)
current_node = 0 # 仓库节点
available_nodes = list(range(1, num_nodes)) # 可选节点
path = [current_node]
total_distance = 0
while available_nodes:
next_node = select_next_node(pheromone, current_node, available_nodes, alpha, beta)
path.append(next_node)
total_distance += calculate_distance(data[current_node], data[next_node])
available_nodes.remove(next_node)
current_node = next_node
path.append(0) # 返回仓库节点
total_distance += calculate_distance(data[current_node], data[0])
return path, total_distance
# 更新最佳路径
def update_best_path(path, total_distance, best_ant):
if total_distance < best_ant['total_distance']:
best_ant['path'] = path
best_ant['total_distance'] = total_distance
# 蚁群算法求解VRPTW
def solve_vrptw(data, num_vehicles, vehicle_capacity, num_ants, num_iterations, alpha, beta, evaporation_rate, Q):
num_nodes = len(data)
best_ant = {
'path': [],
'total_distance': float('inf')
}
pheromone = initialize_pheromone(data)
for _ in range(num_iterations):
ants = []
for _ in range(num_ants):
ant = {}
ant['path'], ant['total_distance'] = construct_initial_path(pheromone, alpha, beta)
ants.append(ant)
update_best_path(ant['path'], ant['total_distance'], best_ant)
pheromone = update_pheromone(pheromone, ants, best_ant, evaporation_rate, Q)
return best_ant['path'], best_ant['total_distance']
# 主函数
if __name__ == '__main__':
data, num_vehicles, vehicle_capacity = read_data('C101.txt')
num_ants = 10 # 蚂蚁数量
num_iterations = 100 # 迭代次数
alpha = 1 # 信息素重要程度参数
beta = 2 # 距离的重要程度参数
evaporation_rate = 0.5 # 信息素蒸发率
Q = 100 # 信息素增强强度
best_path, best_distance = solve_vrptw(data, num_vehicles, vehicle_capacity, num_ants, num_iterations, alpha, beta, evaporation_rate, Q)
print('最佳路径:', best_path)
print('最佳路径总距离:', best_distance)