route-optimization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRoute Optimization
路径优化
You are an expert in transportation route optimization and vehicle routing problems. Your goal is to help design optimal delivery routes that minimize costs, reduce travel distance, improve service levels, and maximize fleet utilization.
您是运输路径优化和车辆路径问题领域的专家。您的目标是帮助设计最优配送路线,以降低成本、缩短行驶距离、提升服务水平并最大化车队利用率。
Initial Assessment
初始评估
Before optimizing routes, understand:
-
Business Context
- What type of operation? (delivery, pickup, field service)
- Fleet size and vehicle types?
- Current routing method? (manual, basic software, advanced)
- Primary pain points? (cost, late deliveries, driver overtime)
-
Operational Constraints
- Delivery time windows? (hard/soft constraints)
- Vehicle capacities (weight, volume, pallets)?
- Driver shift lengths and break requirements?
- Service time at each stop?
- Maximum route duration?
-
Network Characteristics
- Number of stops per day?
- Depot locations (single/multiple)?
- Geographic spread? (urban, rural, mixed)
- Traffic patterns and considerations?
- Access restrictions (truck routes, height limits)?
-
Service Requirements
- On-time delivery targets?
- Customer priorities or preferences?
- Special handling needs?
- Real-time changes and dynamic requests?
在优化路线前,需了解以下信息:
-
业务背景
- 运营类型是什么?(配送、取货、外勤服务)
- 车队规模和车辆类型?
- 当前的路径规划方式?(手动、基础软件、高级系统)
- 主要痛点是什么?(成本高、配送延迟、司机超时)
-
运营约束
- 是否有配送时间窗?(硬性/软性约束)
- 车辆容量限制(重量、体积、托盘数)?
- 司机班次时长和休息要求?
- 每个站点的服务时间?
- 最长路线时长?
-
网络特征
- 每日站点数量?
- 仓库位置(单个/多个)?
- 地理覆盖范围?(城市、乡村、混合区域)
- 交通模式及需要考虑的因素?
- 通行限制(货车专用道、高度限制)?
-
服务要求
- 准时配送目标?
- 客户优先级或偏好?
- 特殊处理需求?
- 是否需要应对实时变化和动态请求?
Route Optimization Framework
路径优化框架
Problem Classification
问题分类
1. Traveling Salesman Problem (TSP)
- Single vehicle, visit all locations once
- Return to origin
- Minimize total distance/time
- Use cases: Small deliveries, service routes
2. Vehicle Routing Problem (VRP)
- Multiple vehicles from depot
- Each customer visited once
- Capacity constraints
- Minimize total fleet distance/cost
3. VRP with Time Windows (VRPTW)
- Customers have delivery time windows
- Hard constraints (must arrive in window)
- Soft constraints (preference, with penalty)
- Most common real-world variant
4. Capacitated VRP (CVRP)
- Vehicle capacity limits (weight/volume)
- Cannot exceed capacity on route
- May require return trips
5. Multi-Depot VRP (MDVRP)
- Multiple starting locations
- Assign customers to depots
- Optimize depot selection + routes
6. VRP with Pickup and Delivery (VRPPD)
- Both pickup and delivery at stops
- Precedence constraints
- Simultaneous or sequential
7. Dynamic VRP
- Real-time order insertions
- Route re-optimization during day
- Handle urgent/emergency requests
1. 旅行商问题(Traveling Salesman Problem, TSP)
- 单辆车,访问所有地点一次
- 返回起点
- 最小化总距离/时间
- 适用场景:小型配送、服务路线
2. 车辆路径问题(Vehicle Routing Problem, VRP)
- 多辆车从仓库出发
- 每个客户仅被访问一次
- 存在容量约束
- 最小化车队总行驶距离/成本
3. 带时间窗的车辆路径问题(VRP with Time Windows, VRPTW)
- 客户有配送时间窗
- 硬性约束(必须在时间窗内到达)
- 软性约束(优先满足,违反有惩罚)
- 最常见的实际场景变体
4. 带容量约束的车辆路径问题(Capacitated VRP, CVRP)
- 车辆有容量限制(重量/体积)
- 路线上的负载不能超过容量
- 可能需要返程
5. 多仓库车辆路径问题(Multi-Depot VRP, MDVRP)
- 多个起点仓库
- 将客户分配至对应仓库
- 优化仓库选择 + 路线规划
6. 带取货和配送的车辆路径问题(VRP with Pickup and Delivery, VRPPD)
- 站点同时存在取货和配送需求
- 存在优先级约束
- 可同时或按顺序处理
7. 动态车辆路径问题(Dynamic VRP)
- 实时插入新订单
- 日间路线重新优化
- 处理紧急请求
Optimization Methods
优化方法
Exact Methods
精确方法
Branch and Bound
- Guarantees optimal solution
- Only feasible for small problems (<50 stops)
- Exponential time complexity
Mixed-Integer Programming (MIP)
- Mathematical optimization model
- Commercial solvers (Gurobi, CPLEX)
python
from pulp import *
import numpy as np
def vrp_mip_model(distances, demands, vehicle_capacity, num_vehicles):
"""
Basic VRP using Mixed-Integer Programming
Parameters:
- distances: NxN distance matrix
- demands: list of demands at each location
- vehicle_capacity: max capacity per vehicle
- num_vehicles: number of available vehicles
"""
n = len(distances) # includes depot at index 0
customers = range(1, n)
all_nodes = range(n)
vehicles = range(num_vehicles)
# Create problem
prob = LpProblem("VRP", LpMinimize)
# Decision variables
# x[i,j,k] = 1 if vehicle k travels from i to j
x = LpVariable.dicts("route",
[(i,j,k) for i in all_nodes
for j in all_nodes
for k in vehicles if i != j],
cat='Binary')
# u[i,k] = position of node i in route of vehicle k (for subtour elimination)
u = LpVariable.dicts("order",
[(i,k) for i in customers for k in vehicles],
lowBound=1,
upBound=n-1,
cat='Integer')
# Objective: minimize total distance
prob += lpSum([distances[i][j] * x[i,j,k]
for i in all_nodes
for j in all_nodes
for k in vehicles
if i != j])
# Constraints
# 1. Each customer visited exactly once
for j in customers:
prob += lpSum([x[i,j,k]
for i in all_nodes
for k in vehicles
if i != j]) == 1
# 2. Flow conservation: enter and leave each node
for k in vehicles:
for j in all_nodes:
prob += (lpSum([x[i,j,k] for i in all_nodes if i != j]) ==
lpSum([x[j,i,k] for i in all_nodes if i != j]))
# 3. Each vehicle starts from depot
for k in vehicles:
prob += lpSum([x[0,j,k] for j in customers]) <= 1
# 4. Each vehicle returns to depot
for k in vehicles:
prob += lpSum([x[i,0,k] for i in customers]) <= 1
# 5. Vehicle capacity constraint
for k in vehicles:
prob += lpSum([demands[j] * x[i,j,k]
for i in all_nodes
for j in customers
if i != j]) <= vehicle_capacity
# 6. Subtour elimination (Miller-Tucker-Zemlin)
for i in customers:
for j in customers:
for k in vehicles:
if i != j:
prob += u[i,k] - u[j,k] + n * x[i,j,k] <= n - 1
# Solve
prob.solve(PULP_CBC_CMD(msg=1))
# Extract routes
routes = extract_routes(x, vehicles, n)
return {
'status': LpStatus[prob.status],
'total_distance': value(prob.objective),
'routes': routes
}
def extract_routes(x, vehicles, n):
"""Extract route sequences from solution"""
routes = []
for k in vehicles:
route = [0] # start at depot
current = 0
while True:
for j in range(n):
if j != current and (current,j,k) in x:
if x[current,j,k].varValue > 0.5:
if j == 0: # back to depot
route.append(0)
if len(route) > 2: # non-empty route
routes.append(route)
current = -1
break
else:
route.append(j)
current = j
break
if current == -1 or current == 0:
break
return routes分支定界法(Branch and Bound)
- 保证得到最优解
- 仅适用于小规模问题(<50个站点)
- 时间复杂度为指数级
混合整数规划(Mixed-Integer Programming, MIP)
- 数学优化模型
- 商业求解器(Gurobi、CPLEX)
python
from pulp import *
import numpy as np
def vrp_mip_model(distances, demands, vehicle_capacity, num_vehicles):
"""
Basic VRP using Mixed-Integer Programming
Parameters:
- distances: NxN distance matrix
- demands: list of demands at each location
- vehicle_capacity: max capacity per vehicle
- num_vehicles: number of available vehicles
"""
n = len(distances) # includes depot at index 0
customers = range(1, n)
all_nodes = range(n)
vehicles = range(num_vehicles)
# Create problem
prob = LpProblem("VRP", LpMinimize)
# Decision variables
# x[i,j,k] = 1 if vehicle k travels from i to j
x = LpVariable.dicts("route",
[(i,j,k) for i in all_nodes
for j in all_nodes
for k in vehicles if i != j],
cat='Binary')
# u[i,k] = position of node i in route of vehicle k (for subtour elimination)
u = LpVariable.dicts("order",
[(i,k) for i in customers for k in vehicles],
lowBound=1,
upBound=n-1,
cat='Integer')
# Objective: minimize total distance
prob += lpSum([distances[i][j] * x[i,j,k]
for i in all_nodes
for j in all_nodes
for k in vehicles
if i != j])
# Constraints
# 1. Each customer visited exactly once
for j in customers:
prob += lpSum([x[i,j,k]
for i in all_nodes
for k in vehicles
if i != j]) == 1
# 2. Flow conservation: enter and leave each node
for k in vehicles:
for j in all_nodes:
prob += (lpSum([x[i,j,k] for i in all_nodes if i != j]) ==
lpSum([x[j,i,k] for i in all_nodes if i != j]))
# 3. Each vehicle starts from depot
for k in vehicles:
prob += lpSum([x[0,j,k] for j in customers]) <= 1
# 4. Each vehicle returns to depot
for k in vehicles:
prob += lpSum([x[i,0,k] for i in customers]) <= 1
# 5. Vehicle capacity constraint
for k in vehicles:
prob += lpSum([demands[j] * x[i,j,k]
for i in all_nodes
for j in customers
if i != j]) <= vehicle_capacity
# 6. Subtour elimination (Miller-Tucker-Zemlin)
for i in customers:
for j in customers:
for k in vehicles:
if i != j:
prob += u[i,k] - u[j,k] + n * x[i,j,k] <= n - 1
# Solve
prob.solve(PULP_CBC_CMD(msg=1))
# Extract routes
routes = extract_routes(x, vehicles, n)
return {
'status': LpStatus[prob.status],
'total_distance': value(prob.objective),
'routes': routes
}
def extract_routes(x, vehicles, n):
"""Extract route sequences from solution"""
routes = []
for k in vehicles:
route = [0] # start at depot
current = 0
while True:
for j in range(n):
if j != current and (current,j,k) in x:
if x[current,j,k].varValue > 0.5:
if j == 0: # back to depot
route.append(0)
if len(route) > 2: # non-empty route
routes.append(route)
current = -1
break
else:
route.append(j)
current = j
break
if current == -1 or current == 0:
break
return routesHeuristic Methods
启发式方法
Nearest Neighbor
- Start at depot, always visit nearest unvisited customer
- Fast but often suboptimal (10-30% worse than optimal)
python
import numpy as np
def nearest_neighbor_vrp(distance_matrix, demands, capacity, depot=0):
"""
Nearest neighbor heuristic for VRP
Simple construction heuristic - builds routes by always
selecting the nearest unvisited customer
"""
n = len(distance_matrix)
unvisited = set(range(1, n)) # exclude depot
routes = []
while unvisited:
route = [depot]
current_load = 0
current_location = depot
while unvisited:
# Find nearest feasible customer
nearest = None
nearest_dist = float('inf')
for customer in unvisited:
if current_load + demands[customer] <= capacity:
dist = distance_matrix[current_location][customer]
if dist < nearest_dist:
nearest = customer
nearest_dist = dist
if nearest is None:
break # No feasible customer, return to depot
route.append(nearest)
current_load += demands[nearest]
current_location = nearest
unvisited.remove(nearest)
route.append(depot) # return to depot
routes.append(route)
total_distance = sum(calculate_route_distance(route, distance_matrix)
for route in routes)
return {
'routes': routes,
'total_distance': total_distance,
'num_vehicles': len(routes)
}
def calculate_route_distance(route, distance_matrix):
"""Calculate total distance of a route"""
distance = 0
for i in range(len(route) - 1):
distance += distance_matrix[route[i]][route[i+1]]
return distanceSavings Algorithm (Clarke-Wright)
- Merge routes based on savings from combining
- Industry standard heuristic
- Good balance of speed and quality
python
def clarke_wright_savings(distance_matrix, demands, capacity):
"""
Clarke-Wright Savings Algorithm for VRP
Starts with individual routes to each customer,
then merges routes based on savings
"""
n = len(distance_matrix)
depot = 0
# Calculate savings for all customer pairs
savings = []
for i in range(1, n):
for j in range(i+1, n):
saving = (distance_matrix[depot][i] +
distance_matrix[depot][j] -
distance_matrix[i][j])
savings.append((saving, i, j))
# Sort savings in descending order
savings.sort(reverse=True, key=lambda x: x[0])
# Initialize: each customer has its own route
routes = {i: [depot, i, depot] for i in range(1, n)}
route_loads = {i: demands[i] for i in range(1, n)}
# Try to merge routes based on savings
for saving_value, i, j in savings:
# Find routes containing i and j
route_i = None
route_j = None
for route_id, route in routes.items():
if i in route:
route_i = route_id
if j in route:
route_j = route_id
# Can only merge if i and j are in different routes
if route_i != route_j:
route_i_obj = routes[route_i]
route_j_obj = routes[route_j]
# Check if merging is feasible
combined_load = route_loads[route_i] + route_loads[route_j]
if combined_load <= capacity:
# Check if i and j are at ends of their routes
i_at_end = (route_i_obj[1] == i or route_i_obj[-2] == i)
j_at_end = (route_j_obj[1] == j or route_j_obj[-2] == j)
if i_at_end and j_at_end:
# Merge routes
new_route = merge_routes(route_i_obj, route_j_obj, i, j)
# Update routes
routes[route_i] = new_route
route_loads[route_i] = combined_load
del routes[route_j]
del route_loads[route_j]
return {
'routes': list(routes.values()),
'total_distance': sum(calculate_route_distance(route, distance_matrix)
for route in routes.values()),
'num_vehicles': len(routes)
}
def merge_routes(route1, route2, i, j):
"""Merge two routes through nodes i and j"""
# Remove depot endpoints
r1 = route1[1:-1]
r2 = route2[1:-1]
# Orient routes so i and j connect properly
if r1[-1] == i and r2[0] == j:
merged = [0] + r1 + r2 + [0]
elif r1[-1] == i and r2[-1] == j:
merged = [0] + r1 + r2[::-1] + [0]
elif r1[0] == i and r2[0] == j:
merged = [0] + r1[::-1] + r2 + [0]
elif r1[0] == i and r2[-1] == j:
merged = [0] + r2 + r1 + [0]
else:
merged = [0] + r1 + r2 + [0] # fallback
return mergedSweep Algorithm
- Sort customers by angle from depot
- Build routes by sweeping around depot
- Good for clustered customers
python
import math
def sweep_algorithm(coordinates, demands, capacity, depot_idx=0):
"""
Sweep Algorithm for VRP
Sorts customers by polar angle from depot,
then builds routes by sweeping clockwise
"""
depot = coordinates[depot_idx]
n = len(coordinates)
# Calculate angles from depot
customers = []
for i in range(n):
if i != depot_idx:
dx = coordinates[i][0] - depot[0]
dy = coordinates[i][1] - depot[1]
angle = math.atan2(dy, dx)
customers.append((i, angle, demands[i]))
# Sort by angle
customers.sort(key=lambda x: x[1])
# Build routes by sweeping
routes = []
current_route = [depot_idx]
current_load = 0
for customer_id, angle, demand in customers:
if current_load + demand <= capacity:
current_route.append(customer_id)
current_load += demand
else:
# Start new route
current_route.append(depot_idx)
routes.append(current_route)
current_route = [depot_idx, customer_id]
current_load = demand
# Add final route
if len(current_route) > 1:
current_route.append(depot_idx)
routes.append(current_route)
return {
'routes': routes,
'num_vehicles': len(routes)
}最近邻算法(Nearest Neighbor)
- 从仓库出发,始终访问最近的未访问客户
- 速度快但通常不是最优解(比最优解差10-30%)
python
import numpy as np
def nearest_neighbor_vrp(distance_matrix, demands, capacity, depot=0):
"""
Nearest neighbor heuristic for VRP
Simple construction heuristic - builds routes by always
selecting the nearest unvisited customer
"""
n = len(distance_matrix)
unvisited = set(range(1, n)) # exclude depot
routes = []
while unvisited:
route = [depot]
current_load = 0
current_location = depot
while unvisited:
# Find nearest feasible customer
nearest = None
nearest_dist = float('inf')
for customer in unvisited:
if current_load + demands[customer] <= capacity:
dist = distance_matrix[current_location][customer]
if dist < nearest_dist:
nearest = customer
nearest_dist = dist
if nearest is None:
break # No feasible customer, return to depot
route.append(nearest)
current_load += demands[nearest]
current_location = nearest
unvisited.remove(nearest)
route.append(depot) # return to depot
routes.append(route)
total_distance = sum(calculate_route_distance(route, distance_matrix)
for route in routes)
return {
'routes': routes,
'total_distance': total_distance,
'num_vehicles': len(routes)
}
def calculate_route_distance(route, distance_matrix):
"""Calculate total distance of a route"""
distance = 0
for i in range(len(route) - 1):
distance += distance_matrix[route[i]][route[i+1]]
return distance节约算法(Clarke-Wright Savings Algorithm)
- 基于合并路线的节约值进行路线合并
- 行业标准启发式算法
- 在速度和质量之间达到良好平衡
python
def clarke_wright_savings(distance_matrix, demands, capacity):
"""
Clarke-Wright Savings Algorithm for VRP
Starts with individual routes to each customer,
then merges routes based on savings
"""
n = len(distance_matrix)
depot = 0
# Calculate savings for all customer pairs
savings = []
for i in range(1, n):
for j in range(i+1, n):
saving = (distance_matrix[depot][i] +
distance_matrix[depot][j] -
distance_matrix[i][j])
savings.append((saving, i, j))
# Sort savings in descending order
savings.sort(reverse=True, key=lambda x: x[0])
# Initialize: each customer has its own route
routes = {i: [depot, i, depot] for i in range(1, n)}
route_loads = {i: demands[i] for i in range(1, n)}
# Try to merge routes based on savings
for saving_value, i, j in savings:
# Find routes containing i and j
route_i = None
route_j = None
for route_id, route in routes.items():
if i in route:
route_i = route_id
if j in route:
route_j = route_id
# Can only merge if i and j are in different routes
if route_i != route_j:
route_i_obj = routes[route_i]
route_j_obj = routes[route_j]
# Check if merging is feasible
combined_load = route_loads[route_i] + route_loads[route_j]
if combined_load <= capacity:
# Check if i and j are at ends of their routes
i_at_end = (route_i_obj[1] == i or route_i_obj[-2] == i)
j_at_end = (route_j_obj[1] == j or route_j_obj[-2] == j)
if i_at_end and j_at_end:
# Merge routes
new_route = merge_routes(route_i_obj, route_j_obj, i, j)
# Update routes
routes[route_i] = new_route
route_loads[route_i] = combined_load
del routes[route_j]
del route_loads[route_j]
return {
'routes': list(routes.values()),
'total_distance': sum(calculate_route_distance(route, distance_matrix)
for route in routes.values()),
'num_vehicles': len(routes)
}
def merge_routes(route1, route2, i, j):
"""Merge two routes through nodes i and j"""
# Remove depot endpoints
r1 = route1[1:-1]
r2 = route2[1:-1]
# Orient routes so i and j connect properly
if r1[-1] == i and r2[0] == j:
merged = [0] + r1 + r2 + [0]
elif r1[-1] == i and r2[-1] == j:
merged = [0] + r1 + r2[::-1] + [0]
elif r1[0] == i and r2[0] == j:
merged = [0] + r1[::-1] + r2 + [0]
elif r1[0] == i and r2[-1] == j:
merged = [0] + r2 + r1 + [0]
else:
merged = [0] + r1 + r2 + [0] # fallback
return merged扫描算法(Sweep Algorithm)
- 按客户相对于仓库的角度排序
- 通过围绕仓库扫描来构建路线
- 适用于客户集中分布的场景
python
import math
def sweep_algorithm(coordinates, demands, capacity, depot_idx=0):
"""
Sweep Algorithm for VRP
Sorts customers by polar angle from depot,
then builds routes by sweeping clockwise
"""
depot = coordinates[depot_idx]
n = len(coordinates)
# Calculate angles from depot
customers = []
for i in range(n):
if i != depot_idx:
dx = coordinates[i][0] - depot[0]
dy = coordinates[i][1] - depot[1]
angle = math.atan2(dy, dx)
customers.append((i, angle, demands[i]))
# Sort by angle
customers.sort(key=lambda x: x[1])
# Build routes by sweeping
routes = []
current_route = [depot_idx]
current_load = 0
for customer_id, angle, demand in customers:
if current_load + demand <= capacity:
current_route.append(customer_id)
current_load += demand
else:
# Start new route
current_route.append(depot_idx)
routes.append(current_route)
current_route = [depot_idx, customer_id]
current_load = demand
# Add final route
if len(current_route) > 1:
current_route.append(depot_idx)
routes.append(current_route)
return {
'routes': routes,
'num_vehicles': len(routes)
}Metaheuristic Methods
元启发式方法
Simulated Annealing
- Probabilistically accept worse solutions
- Escape local optima
- Gradually reduce "temperature"
python
import random
import math
def simulated_annealing_vrp(initial_solution, distance_matrix,
demands, capacity,
initial_temp=1000, cooling_rate=0.995,
iterations=10000):
"""
Simulated Annealing for VRP
Improves initial solution through iterative neighbor search
with probabilistic acceptance of worse solutions
"""
current_solution = initial_solution.copy()
current_cost = calculate_total_distance(current_solution, distance_matrix)
best_solution = current_solution.copy()
best_cost = current_cost
temperature = initial_temp
for iteration in range(iterations):
# Generate neighbor solution
neighbor = generate_neighbor(current_solution, demands, capacity)
neighbor_cost = calculate_total_distance(neighbor, distance_matrix)
# Calculate acceptance probability
delta = neighbor_cost - current_cost
if delta < 0:
# Better solution, always accept
accept = True
else:
# Worse solution, accept with probability
accept_prob = math.exp(-delta / temperature)
accept = random.random() < accept_prob
if accept:
current_solution = neighbor
current_cost = neighbor_cost
if current_cost < best_cost:
best_solution = current_solution.copy()
best_cost = current_cost
# Cool down
temperature *= cooling_rate
return {
'routes': best_solution,
'total_distance': best_cost
}
def generate_neighbor(routes, demands, capacity):
"""Generate neighbor solution using local search operators"""
neighbor = [route.copy() for route in routes]
operator = random.choice(['swap', 'relocate', '2-opt'])
if operator == 'swap' and len(neighbor) >= 2:
# Swap customers between two routes
route1_idx = random.randint(0, len(neighbor) - 1)
route2_idx = random.randint(0, len(neighbor) - 1)
while route2_idx == route1_idx:
route2_idx = random.randint(0, len(neighbor) - 1)
route1 = neighbor[route1_idx]
route2 = neighbor[route2_idx]
if len(route1) > 2 and len(route2) > 2:
pos1 = random.randint(1, len(route1) - 2)
pos2 = random.randint(1, len(route2) - 2)
route1[pos1], route2[pos2] = route2[pos2], route1[pos1]
elif operator == 'relocate':
# Move customer from one route to another
if len(neighbor) >= 2:
route1_idx = random.randint(0, len(neighbor) - 1)
route2_idx = random.randint(0, len(neighbor) - 1)
while route2_idx == route1_idx:
route2_idx = random.randint(0, len(neighbor) - 1)
route1 = neighbor[route1_idx]
route2 = neighbor[route2_idx]
if len(route1) > 2:
pos1 = random.randint(1, len(route1) - 2)
customer = route1.pop(pos1)
pos2 = random.randint(1, len(route2) - 1)
route2.insert(pos2, customer)
elif operator == '2-opt':
# 2-opt within a single route
route_idx = random.randint(0, len(neighbor) - 1)
route = neighbor[route_idx]
if len(route) > 4:
i = random.randint(1, len(route) - 3)
j = random.randint(i + 1, len(route) - 2)
route[i:j+1] = reversed(route[i:j+1])
return neighbor
def calculate_total_distance(routes, distance_matrix):
"""Calculate total distance across all routes"""
total = 0
for route in routes:
for i in range(len(route) - 1):
total += distance_matrix[route[i]][route[i+1]]
return totalGenetic Algorithm
- Population-based search
- Crossover and mutation operators
- Good for large problems
Ant Colony Optimization (ACO)
- Probabilistic construction of solutions
- Pheromone trails guide search
- Good quality solutions
Large Neighborhood Search (LNS)
- Destroy and repair parts of solution
- Very effective for large problems
模拟退火算法(Simulated Annealing)
- 以概率接受较差的解
- 跳出局部最优
- 逐渐降低“温度”
python
import random
import math
def simulated_annealing_vrp(initial_solution, distance_matrix,
demands, capacity,
initial_temp=1000, cooling_rate=0.995,
iterations=10000):
"""
Simulated Annealing for VRP
Improves initial solution through iterative neighbor search
with probabilistic acceptance of worse solutions
"""
current_solution = initial_solution.copy()
current_cost = calculate_total_distance(current_solution, distance_matrix)
best_solution = current_solution.copy()
best_cost = current_cost
temperature = initial_temp
for iteration in range(iterations):
# Generate neighbor solution
neighbor = generate_neighbor(current_solution, demands, capacity)
neighbor_cost = calculate_total_distance(neighbor, distance_matrix)
# Calculate acceptance probability
delta = neighbor_cost - current_cost
if delta < 0:
# Better solution, always accept
accept = True
else:
# Worse solution, accept with probability
accept_prob = math.exp(-delta / temperature)
accept = random.random() < accept_prob
if accept:
current_solution = neighbor
current_cost = neighbor_cost
if current_cost < best_cost:
best_solution = current_solution.copy()
best_cost = current_cost
# Cool down
temperature *= cooling_rate
return {
'routes': best_solution,
'total_distance': best_cost
}
def generate_neighbor(routes, demands, capacity):
"""Generate neighbor solution using local search operators"""
neighbor = [route.copy() for route in routes]
operator = random.choice(['swap', 'relocate', '2-opt'])
if operator == 'swap' and len(neighbor) >= 2:
# Swap customers between two routes
route1_idx = random.randint(0, len(neighbor) - 1)
route2_idx = random.randint(0, len(neighbor) - 1)
while route2_idx == route1_idx:
route2_idx = random.randint(0, len(neighbor) - 1)
route1 = neighbor[route1_idx]
route2 = neighbor[route2_idx]
if len(route1) > 2 and len(route2) > 2:
pos1 = random.randint(1, len(route1) - 2)
pos2 = random.randint(1, len(route2) - 2)
route1[pos1], route2[pos2] = route2[pos2], route1[pos1]
elif operator == 'relocate':
# Move customer from one route to another
if len(neighbor) >= 2:
route1_idx = random.randint(0, len(neighbor) - 1)
route2_idx = random.randint(0, len(neighbor) - 1)
while route2_idx == route1_idx:
route2_idx = random.randint(0, len(neighbor) - 1)
route1 = neighbor[route1_idx]
route2 = neighbor[route2_idx]
if len(route1) > 2:
pos1 = random.randint(1, len(route1) - 2)
customer = route1.pop(pos1)
pos2 = random.randint(1, len(route2) - 1)
route2.insert(pos2, customer)
elif operator == '2-opt':
# 2-opt within a single route
route_idx = random.randint(0, len(neighbor) - 1)
route = neighbor[route_idx]
if len(route) > 4:
i = random.randint(1, len(route) - 3)
j = random.randint(i + 1, len(route) - 2)
route[i:j+1] = reversed(route[i:j+1])
return neighbor
def calculate_total_distance(routes, distance_matrix):
"""Calculate total distance across all routes"""
total = 0
for route in routes:
for i in range(len(route) - 1):
total += distance_matrix[route[i]][route[i+1]]
return total遗传算法(Genetic Algorithm)
- 基于种群的搜索方法
- 包含交叉和变异算子
- 适用于大规模问题
蚁群优化算法(Ant Colony Optimization, ACO)
- 概率性构建解
- 信息素轨迹引导搜索
- 能得到高质量解
大邻域搜索(Large Neighborhood Search, LNS)
- 破坏并修复解的部分内容
- 对大规模问题非常有效
Advanced VRP Variants
高级VRP变体
VRP with Time Windows (VRPTW)
带时间窗的车辆路径问题(VRPTW)
python
def vrptw_earliest_start_heuristic(customers, vehicles, time_windows, service_times, travel_times):
"""
VRPTW heuristic: insert customers based on earliest feasible start time
Parameters:
- customers: list of customer IDs
- vehicles: list of vehicle objects
- time_windows: dict {customer_id: (earliest, latest)}
- service_times: dict {customer_id: service_duration}
- travel_times: function(from_loc, to_loc) -> travel_time
"""
routes = []
unserved = set(customers)
for vehicle in vehicles:
if not unserved:
break
route = [vehicle.depot]
current_time = 0
current_location = vehicle.depot
route_load = 0
while unserved:
# Find best customer to insert
best_customer = None
best_start_time = float('inf')
for customer in unserved:
# Check capacity
if route_load + customer.demand > vehicle.capacity:
continue
# Calculate arrival time
travel_time = travel_times(current_location, customer.location)
arrival_time = current_time + travel_time
# Check time window feasibility
earliest, latest = time_windows[customer.id]
if arrival_time > latest:
continue # Too late
start_time = max(arrival_time, earliest)
if start_time < best_start_time:
best_start_time = start_time
best_customer = customer
if best_customer is None:
break # No feasible customer
# Add customer to route
route.append(best_customer)
unserved.remove(best_customer)
travel_time = travel_times(current_location, best_customer.location)
arrival_time = current_time + travel_time
earliest, latest = time_windows[best_customer.id]
current_time = max(arrival_time, earliest) + service_times[best_customer.id]
current_location = best_customer.location
route_load += best_customer.demand
route.append(vehicle.depot)
routes.append(route)
return routespython
def vrptw_earliest_start_heuristic(customers, vehicles, time_windows, service_times, travel_times):
"""
VRPTW heuristic: insert customers based on earliest feasible start time
Parameters:
- customers: list of customer IDs
- vehicles: list of vehicle objects
- time_windows: dict {customer_id: (earliest, latest)}
- service_times: dict {customer_id: service_duration}
- travel_times: function(from_loc, to_loc) -> travel_time
"""
routes = []
unserved = set(customers)
for vehicle in vehicles:
if not unserved:
break
route = [vehicle.depot]
current_time = 0
current_location = vehicle.depot
route_load = 0
while unserved:
# Find best customer to insert
best_customer = None
best_start_time = float('inf')
for customer in unserved:
# Check capacity
if route_load + customer.demand > vehicle.capacity:
continue
# Calculate arrival time
travel_time = travel_times(current_location, customer.location)
arrival_time = current_time + travel_time
# Check time window feasibility
earliest, latest = time_windows[customer.id]
if arrival_time > latest:
continue # Too late
start_time = max(arrival_time, earliest)
if start_time < best_start_time:
best_start_time = start_time
best_customer = customer
if best_customer is None:
break # No feasible customer
# Add customer to route
route.append(best_customer)
unserved.remove(best_customer)
travel_time = travel_times(current_location, best_customer.location)
arrival_time = current_time + travel_time
earliest, latest = time_windows[best_customer.id]
current_time = max(arrival_time, earliest) + service_times[best_customer.id]
current_location = best_customer.location
route_load += best_customer.demand
route.append(vehicle.depot)
routes.append(route)
return routesMulti-Depot VRP
多仓库车辆路径问题(MDVRP)
python
def assign_customers_to_depots(customers, depots, distance_matrix):
"""
Assign customers to nearest depot for MDVRP
Returns clusters of customers for each depot
"""
depot_clusters = {depot: [] for depot in depots}
for customer in customers:
# Find nearest depot
nearest_depot = None
min_distance = float('inf')
for depot in depots:
dist = distance_matrix[customer][depot]
if dist < min_distance:
min_distance = dist
nearest_depot = depot
depot_clusters[nearest_depot].append(customer)
return depot_clusters
def solve_mdvrp(customers, depots, distance_matrix, demands, capacity):
"""
Solve Multi-Depot VRP
1. Assign customers to depots
2. Solve VRP for each depot independently
"""
# Assign customers to depots
clusters = assign_customers_to_depots(customers, depots, distance_matrix)
all_routes = []
total_distance = 0
for depot, depot_customers in clusters.items():
if not depot_customers:
continue
# Solve VRP for this depot
depot_solution = clarke_wright_savings(
distance_matrix,
demands,
capacity
)
all_routes.extend(depot_solution['routes'])
total_distance += depot_solution['total_distance']
return {
'routes': all_routes,
'total_distance': total_distance,
'depot_clusters': clusters
}python
def assign_customers_to_depots(customers, depots, distance_matrix):
"""
Assign customers to nearest depot for MDVRP
Returns clusters of customers for each depot
"""
depot_clusters = {depot: [] for depot in depots}
for customer in customers:
# Find nearest depot
nearest_depot = None
min_distance = float('inf')
for depot in depots:
dist = distance_matrix[customer][depot]
if dist < min_distance:
min_distance = dist
nearest_depot = depot
depot_clusters[nearest_depot].append(customer)
return depot_clusters
def solve_mdvrp(customers, depots, distance_matrix, demands, capacity):
"""
Solve Multi-Depot VRP
1. Assign customers to depots
2. Solve VRP for each depot independently
"""
# Assign customers to depots
clusters = assign_customers_to_depots(customers, depots, distance_matrix)
all_routes = []
total_distance = 0
for depot, depot_customers in clusters.items():
if not depot_customers:
continue
# Solve VRP for this depot
depot_solution = clarke_wright_savings(
distance_matrix,
demands,
capacity
)
all_routes.extend(depot_solution['routes'])
total_distance += depot_solution['total_distance']
return {
'routes': all_routes,
'total_distance': total_distance,
'depot_clusters': clusters
}Dynamic VRP (Real-Time Routing)
动态车辆路径问题(实时路径规划)
python
class DynamicVRPSolver:
"""
Dynamic VRP solver for real-time order insertions
Handles new orders arriving during route execution
"""
def __init__(self, initial_routes, distance_matrix):
self.routes = initial_routes
self.distance_matrix = distance_matrix
self.completed_stops = set()
def insert_urgent_order(self, new_customer, current_vehicle_positions):
"""
Insert urgent order into existing routes
Uses cheapest insertion heuristic
"""
best_route_idx = None
best_position = None
min_cost_increase = float('inf')
for route_idx, route in enumerate(self.routes):
# Get current position of vehicle on this route
current_pos = current_vehicle_positions.get(route_idx, 0)
# Try inserting at each position after current
for insert_pos in range(current_pos + 1, len(route)):
# Calculate cost increase
prev_customer = route[insert_pos - 1]
next_customer = route[insert_pos]
current_cost = self.distance_matrix[prev_customer][next_customer]
new_cost = (self.distance_matrix[prev_customer][new_customer] +
self.distance_matrix[new_customer][next_customer])
cost_increase = new_cost - current_cost
if cost_increase < min_cost_increase:
min_cost_increase = cost_increase
best_route_idx = route_idx
best_position = insert_pos
if best_route_idx is not None:
self.routes[best_route_idx].insert(best_position, new_customer)
return {
'success': True,
'route': best_route_idx,
'position': best_position,
'cost_increase': min_cost_increase
}
else:
# Need new route
self.routes.append([0, new_customer, 0])
return {
'success': True,
'route': len(self.routes) - 1,
'position': 1,
'new_route': True
}
def mark_completed(self, customer):
"""Mark customer as completed"""
self.completed_stops.add(customer)
def reoptimize_remaining(self):
"""Re-optimize remaining stops on all routes"""
for route in self.routes:
# Remove completed stops
route[:] = [stop for stop in route
if stop not in self.completed_stops or stop == 0]
# Remove empty routes
self.routes = [route for route in self.routes if len(route) > 2]python
class DynamicVRPSolver:
"""
Dynamic VRP solver for real-time order insertions
Handles new orders arriving during route execution
"""
def __init__(self, initial_routes, distance_matrix):
self.routes = initial_routes
self.distance_matrix = distance_matrix
self.completed_stops = set()
def insert_urgent_order(self, new_customer, current_vehicle_positions):
"""
Insert urgent order into existing routes
Uses cheapest insertion heuristic
"""
best_route_idx = None
best_position = None
min_cost_increase = float('inf')
for route_idx, route in enumerate(self.routes):
# Get current position of vehicle on this route
current_pos = current_vehicle_positions.get(route_idx, 0)
# Try inserting at each position after current
for insert_pos in range(current_pos + 1, len(route)):
# Calculate cost increase
prev_customer = route[insert_pos - 1]
next_customer = route[insert_pos]
current_cost = self.distance_matrix[prev_customer][next_customer]
new_cost = (self.distance_matrix[prev_customer][new_customer] +
self.distance_matrix[new_customer][next_customer])
cost_increase = new_cost - current_cost
if cost_increase < min_cost_increase:
min_cost_increase = cost_increase
best_route_idx = route_idx
best_position = insert_pos
if best_route_idx is not None:
self.routes[best_route_idx].insert(best_position, new_customer)
return {
'success': True,
'route': best_route_idx,
'position': best_position,
'cost_increase': min_cost_increase
}
else:
# Need new route
self.routes.append([0, new_customer, 0])
return {
'success': True,
'route': len(self.routes) - 1,
'position': 1,
'new_route': True
}
def mark_completed(self, customer):
"""Mark customer as completed"""
self.completed_stops.add(customer)
def reoptimize_remaining(self):
"""Re-optimize remaining stops on all routes"""
for route in self.routes:
# Remove completed stops
route[:] = [stop for stop in route
if stop not in self.completed_stops or stop == 0]
# Remove empty routes
self.routes = [route for route in self.routes if len(route) > 2]Practical Implementation
实际实现
Complete VRP Solver Class
完整VRP求解器类
python
import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
class VRPSolver:
"""
Comprehensive VRP solver with multiple algorithms
"""
def __init__(self, coordinates, demands, vehicle_capacity,
num_vehicles=None, depot_idx=0):
"""
Initialize VRP solver
Parameters:
- coordinates: list of (x, y) tuples for locations
- demands: list of demand at each location
- vehicle_capacity: maximum capacity per vehicle
- num_vehicles: max vehicles (None = unlimited)
- depot_idx: index of depot location (default 0)
"""
self.coordinates = np.array(coordinates)
self.demands = np.array(demands)
self.vehicle_capacity = vehicle_capacity
self.num_vehicles = num_vehicles
self.depot_idx = depot_idx
# Calculate distance matrix
self.distance_matrix = cdist(self.coordinates, self.coordinates,
metric='euclidean')
self.best_solution = None
self.best_cost = float('inf')
def solve(self, method='clarke_wright', **kwargs):
"""
Solve VRP using specified method
Methods:
- 'nearest_neighbor': Fast, simple heuristic
- 'clarke_wright': Savings algorithm
- 'sweep': Angular sweep from depot
- 'genetic': Genetic algorithm (slower, better quality)
"""
if method == 'nearest_neighbor':
solution = nearest_neighbor_vrp(
self.distance_matrix,
self.demands,
self.vehicle_capacity,
self.depot_idx
)
elif method == 'clarke_wright':
solution = clarke_wright_savings(
self.distance_matrix,
self.demands,
self.vehicle_capacity
)
elif method == 'sweep':
solution = sweep_algorithm(
self.coordinates,
self.demands,
self.vehicle_capacity,
self.depot_idx
)
else:
raise ValueError(f"Unknown method: {method}")
# Improve solution with local search
if kwargs.get('improve', True):
solution = self.local_search_improvement(solution['routes'])
self.best_solution = solution
self.best_cost = solution['total_distance']
return solution
def local_search_improvement(self, routes, iterations=1000):
"""
Improve solution using 2-opt and relocate operators
"""
current_routes = [route.copy() for route in routes]
current_cost = calculate_total_distance(current_routes, self.distance_matrix)
improved = True
iter_count = 0
while improved and iter_count < iterations:
improved = False
iter_count += 1
# Try 2-opt within each route
for route_idx, route in enumerate(current_routes):
if len(route) < 4:
continue
for i in range(1, len(route) - 2):
for j in range(i + 1, len(route) - 1):
# Try reversing segment
new_route = route.copy()
new_route[i:j+1] = reversed(route[i:j+1])
# Check if improvement
old_dist = calculate_route_distance(route, self.distance_matrix)
new_dist = calculate_route_distance(new_route, self.distance_matrix)
if new_dist < old_dist:
current_routes[route_idx] = new_route
current_cost = current_cost - old_dist + new_dist
improved = True
break
if improved:
break
return {
'routes': current_routes,
'total_distance': current_cost,
'num_vehicles': len(current_routes)
}
def visualize(self, solution=None, save_path=None):
"""
Visualize routes on a map
"""
if solution is None:
solution = self.best_solution
if solution is None:
raise ValueError("No solution to visualize")
plt.figure(figsize=(12, 8))
# Plot depot
depot = self.coordinates[self.depot_idx]
plt.plot(depot[0], depot[1], 'rs', markersize=15, label='Depot')
# Plot routes with different colors
colors = plt.cm.tab20(np.linspace(0, 1, len(solution['routes'])))
for idx, route in enumerate(solution['routes']):
route_coords = self.coordinates[route]
plt.plot(route_coords[:, 0], route_coords[:, 1],
'o-', color=colors[idx], linewidth=2, markersize=8,
label=f'Route {idx+1}')
plt.xlabel('X Coordinate')
plt.ylabel('Y Coordinate')
plt.title(f'VRP Solution - Total Distance: {solution["total_distance"]:.2f}')
plt.legend()
plt.grid(True, alpha=0.3)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
plt.show()
def get_route_statistics(self, solution=None):
"""
Calculate detailed statistics for solution
"""
if solution is None:
solution = self.best_solution
stats = {
'num_vehicles': len(solution['routes']),
'total_distance': solution['total_distance'],
'routes': []
}
for idx, route in enumerate(solution['routes']):
route_load = sum(self.demands[i] for i in route if i != self.depot_idx)
route_distance = calculate_route_distance(route, self.distance_matrix)
num_stops = len([i for i in route if i != self.depot_idx])
stats['routes'].append({
'route_id': idx + 1,
'num_stops': num_stops,
'total_load': route_load,
'utilization': route_load / self.vehicle_capacity * 100,
'distance': route_distance,
'sequence': route
})
return statspython
import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
class VRPSolver:
"""
Comprehensive VRP solver with multiple algorithms
"""
def __init__(self, coordinates, demands, vehicle_capacity,
num_vehicles=None, depot_idx=0):
"""
Initialize VRP solver
Parameters:
- coordinates: list of (x, y) tuples for locations
- demands: list of demand at each location
- vehicle_capacity: maximum capacity per vehicle
- num_vehicles: max vehicles (None = unlimited)
- depot_idx: index of depot location (default 0)
"""
self.coordinates = np.array(coordinates)
self.demands = np.array(demands)
self.vehicle_capacity = vehicle_capacity
self.num_vehicles = num_vehicles
self.depot_idx = depot_idx
# Calculate distance matrix
self.distance_matrix = cdist(self.coordinates, self.coordinates,
metric='euclidean')
self.best_solution = None
self.best_cost = float('inf')
def solve(self, method='clarke_wright', **kwargs):
"""
Solve VRP using specified method
Methods:
- 'nearest_neighbor': Fast, simple heuristic
- 'clarke_wright': Savings algorithm
- 'sweep': Angular sweep from depot
- 'genetic': Genetic algorithm (slower, better quality)
"""
if method == 'nearest_neighbor':
solution = nearest_neighbor_vrp(
self.distance_matrix,
self.demands,
self.vehicle_capacity,
self.depot_idx
)
elif method == 'clarke_wright':
solution = clarke_wright_savings(
self.distance_matrix,
self.demands,
self.vehicle_capacity
)
elif method == 'sweep':
solution = sweep_algorithm(
self.coordinates,
self.demands,
self.vehicle_capacity,
self.depot_idx
)
else:
raise ValueError(f"Unknown method: {method}")
# Improve solution with local search
if kwargs.get('improve', True):
solution = self.local_search_improvement(solution['routes'])
self.best_solution = solution
self.best_cost = solution['total_distance']
return solution
def local_search_improvement(self, routes, iterations=1000):
"""
Improve solution using 2-opt and relocate operators
"""
current_routes = [route.copy() for route in routes]
current_cost = calculate_total_distance(current_routes, self.distance_matrix)
improved = True
iter_count = 0
while improved and iter_count < iterations:
improved = False
iter_count += 1
# Try 2-opt within each route
for route_idx, route in enumerate(current_routes):
if len(route) < 4:
continue
for i in range(1, len(route) - 2):
for j in range(i + 1, len(route) - 1):
# Try reversing segment
new_route = route.copy()
new_route[i:j+1] = reversed(route[i:j+1])
# Check if improvement
old_dist = calculate_route_distance(route, self.distance_matrix)
new_dist = calculate_route_distance(new_route, self.distance_matrix)
if new_dist < old_dist:
current_routes[route_idx] = new_route
current_cost = current_cost - old_dist + new_dist
improved = True
break
if improved:
break
return {
'routes': current_routes,
'total_distance': current_cost,
'num_vehicles': len(current_routes)
}
def visualize(self, solution=None, save_path=None):
"""
Visualize routes on a map
"""
if solution is None:
solution = self.best_solution
if solution is None:
raise ValueError("No solution to visualize")
plt.figure(figsize=(12, 8))
# Plot depot
depot = self.coordinates[self.depot_idx]
plt.plot(depot[0], depot[1], 'rs', markersize=15, label='Depot')
# Plot routes with different colors
colors = plt.cm.tab20(np.linspace(0, 1, len(solution['routes'])))
for idx, route in enumerate(solution['routes']):
route_coords = self.coordinates[route]
plt.plot(route_coords[:, 0], route_coords[:, 1],
'o-', color=colors[idx], linewidth=2, markersize=8,
label=f'Route {idx+1}')
plt.xlabel('X Coordinate')
plt.ylabel('Y Coordinate')
plt.title(f'VRP Solution - Total Distance: {solution["total_distance"]:.2f}')
plt.legend()
plt.grid(True, alpha=0.3)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
plt.show()
def get_route_statistics(self, solution=None):
"""
Calculate detailed statistics for solution
"""
if solution is None:
solution = self.best_solution
stats = {
'num_vehicles': len(solution['routes']),
'total_distance': solution['total_distance'],
'routes': []
}
for idx, route in enumerate(solution['routes']):
route_load = sum(self.demands[i] for i in route if i != self.depot_idx)
route_distance = calculate_route_distance(route, self.distance_matrix)
num_stops = len([i for i in route if i != self.depot_idx])
stats['routes'].append({
'route_id': idx + 1,
'num_stops': num_stops,
'total_load': route_load,
'utilization': route_load / self.vehicle_capacity * 100,
'distance': route_distance,
'sequence': route
})
return statsExample usage
Example usage
if name == "main":
# Sample problem: 20 customers
np.random.seed(42)
num_customers = 20
coordinates = np.random.rand(num_customers + 1, 2) * 100
coordinates[0] = [50, 50] # Depot in center
demands = np.random.randint(5, 20, num_customers + 1)
demands[0] = 0 # Depot has no demand
vehicle_capacity = 50
# Solve
solver = VRPSolver(coordinates, demands, vehicle_capacity)
solution = solver.solve(method='clarke_wright', improve=True)
# Get statistics
stats = solver.get_route_statistics()
print(f"Solution found:")
print(f"Number of vehicles: {stats['num_vehicles']}")
print(f"Total distance: {stats['total_distance']:.2f}")
print("\nRoute details:")
for route_stat in stats['routes']:
print(f" Route {route_stat['route_id']}: "
f"{route_stat['num_stops']} stops, "
f"Load: {route_stat['total_load']} "
f"({route_stat['utilization']:.1f}% utilization), "
f"Distance: {route_stat['distance']:.2f}")
# Visualize
solver.visualize()
---if name == "main":
# Sample problem: 20 customers
np.random.seed(42)
num_customers = 20
coordinates = np.random.rand(num_customers + 1, 2) * 100
coordinates[0] = [50, 50] # Depot in center
demands = np.random.randint(5, 20, num_customers + 1)
demands[0] = 0 # Depot has no demand
vehicle_capacity = 50
# Solve
solver = VRPSolver(coordinates, demands, vehicle_capacity)
solution = solver.solve(method='clarke_wright', improve=True)
# Get statistics
stats = solver.get_route_statistics()
print(f"Solution found:")
print(f"Number of vehicles: {stats['num_vehicles']}")
print(f"Total distance: {stats['total_distance']:.2f}")
print("\nRoute details:")
for route_stat in stats['routes']:
print(f" Route {route_stat['route_id']}: "
f"{route_stat['num_stops']} stops, "
f"Load: {route_stat['total_load']} "
f"({route_stat['utilization']:.1f}% utilization), "
f"Distance: {route_stat['distance']:.2f}")
# Visualize
solver.visualize()
---Commercial Routing Software
商用路径规划软件
Leading Platforms
主流平台
Omnitracs / Roadnet
- Industry standard for route optimization
- Real-time tracking integration
- Dynamic rerouting
Descartes Route Planner
- Multi-depot routing
- Time window optimization
- Territory management
WorkWave Route Manager
- SMB-focused
- Mobile driver app
- GPS tracking
OptimoRoute
- Cloud-based routing
- Real-time order entry
- Driver mobile app
Route4Me
- Multi-stop route optimization
- Territory optimization
- Affordable pricing
Verizon Connect (Networkfleet)
- Telematics integration
- Fleet management features
Teletrac Navman
- Route optimization + telematics
- Compliance tracking
Omnitracs / Roadnet
- 路径优化行业标准
- 集成实时追踪
- 动态重路由
Descartes Route Planner
- 多仓库路径规划
- 时间窗优化
- 区域管理
WorkWave Route Manager
- 面向中小企业
- 司机移动端应用
- GPS追踪
OptimoRoute
- 云端路径规划
- 实时订单录入
- 司机移动端应用
Route4Me
- 多站点路径优化
- 区域优化
- 价格亲民
Verizon Connect (Networkfleet)
- 远程信息处理集成
- 车队管理功能
Teletrac Navman
- 路径优化 + 远程信息处理
- 合规追踪
Python Libraries & Tools
Python库与工具
OR-Tools (Google)
python
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def solve_vrp_ortools(distance_matrix, num_vehicles, depot=0):
"""Solve VRP using Google OR-Tools"""
# Create routing index manager
manager = pywrapcp.RoutingIndexManager(
len(distance_matrix),
num_vehicles,
depot
)
# Create routing model
routing = pywrapcp.RoutingModel(manager)
# Create distance callback
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return int(distance_matrix[from_node][to_node])
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Set search parameters
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
# Solve
solution = routing.SolveWithParameters(search_parameters)
# Extract routes
routes = []
for vehicle_id in range(num_vehicles):
index = routing.Start(vehicle_id)
route = []
while not routing.IsEnd(index):
route.append(manager.IndexToNode(index))
index = solution.Value(routing.NextVar(index))
route.append(manager.IndexToNode(index))
routes.append(route)
return {
'routes': routes,
'total_distance': solution.ObjectiveValue()
}Python VRP Libraries
- : TSP solvers
python-tsp - : VRP with column generation
vrpy - : General optimization
scipy.optimize - : Graph algorithms
networkx - : Distance calculations
geopy
OR-Tools (Google)
python
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def solve_vrp_ortools(distance_matrix, num_vehicles, depot=0):
"""Solve VRP using Google OR-Tools"""
# Create routing index manager
manager = pywrapcp.RoutingIndexManager(
len(distance_matrix),
num_vehicles,
depot
)
# Create routing model
routing = pywrapcp.RoutingModel(manager)
# Create distance callback
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return int(distance_matrix[from_node][to_node])
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Set search parameters
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
# Solve
solution = routing.SolveWithParameters(search_parameters)
# Extract routes
routes = []
for vehicle_id in range(num_vehicles):
index = routing.Start(vehicle_id)
route = []
while not routing.IsEnd(index):
route.append(manager.IndexToNode(index))
index = solution.Value(routing.NextVar(index))
route.append(manager.IndexToNode(index))
routes.append(route)
return {
'routes': routes,
'total_distance': solution.ObjectiveValue()
}Python VRP相关库
- : TSP求解器
python-tsp - : 基于列生成的VRP求解库
vrpy - : 通用优化库
scipy.optimize - : 图算法库
networkx - : 距离计算工具
geopy
Common Challenges & Solutions
常见挑战与解决方案
Challenge: Time Windows Too Tight
挑战:时间窗过紧
Problem:
- No feasible solution exists
- Many late deliveries
Solutions:
- Negotiate wider time windows with customers
- Add more vehicles
- Split large time windows into preferred + acceptable
- Use soft time windows with penalties
- Start routes earlier
- Consider customer priorities (A/B/C)
问题:
- 不存在可行解
- 大量配送延迟
解决方案:
- 与客户协商放宽时间窗
- 增加车辆数量
- 将大时间窗拆分为优先时段+可接受时段
- 使用带惩罚的软性时间窗
- 提前开始路线
- 区分客户优先级(A/B/C类)
Challenge: Unbalanced Routes
挑战:路线负载不均衡
Problem:
- Some vehicles overloaded, others underutilized
- Some routes very long, others very short
Solutions:
- Use post-optimization balancing
- Set min/max stops per route constraints
- Adjust depot territories
- Use max route duration constraints
- Balance by revenue or priority
问题:
- 部分车辆过载,部分车辆利用率低
- 部分路线过长,部分路线过短
解决方案:
- 使用优化后再平衡的策略
- 设置每条路线的最小/最大站点数约束
- 调整仓库覆盖区域
- 设置最长路线时长约束
- 按收入或优先级平衡负载
Challenge: Real-Time Changes
挑战:实时变化
Problem:
- New orders arrive during day
- Customer cancellations
- Traffic delays
Solutions:
- Reserve capacity for dynamic orders (10-20%)
- Use dynamic re-optimization every hour
- Implement tiered service (same-day premium)
- Driver communication system
- Geofence-triggered rerouting
问题:
- 日间新增订单
- 客户取消订单
- 交通延误
解决方案:
- 预留10-20%的容量应对动态订单
- 每小时进行一次动态重优化
- 提供分层服务(当日达溢价)
- 司机沟通系统
- 基于地理围栏触发重路由
Challenge: Driver Compliance
挑战:司机合规性
Problem:
- Drivers don't follow optimal routes
- Take unauthorized breaks
- Skip stops
Solutions:
- Mobile driver app with turn-by-turn
- Gamification (efficiency scores, bonuses)
- Exception reporting and coaching
- GPS tracking with geofences
- Involve drivers in planning (local knowledge)
问题:
- 司机不遵循最优路线
- 擅自休息
- 跳过站点
解决方案:
- 提供带逐向导航的司机移动端应用
- 游戏化激励(效率评分、奖金)
- 异常报告与指导
- 带地理围栏的GPS追踪
- 让司机参与规划(利用本地知识)
Challenge: Traffic Variability
挑战:交通多变性
Problem:
- Static routes don't account for traffic
- Consistent late deliveries
Solutions:
- Use time-dependent travel times
- Historical traffic data integration
- Real-time traffic APIs (Google, HERE)
- Add buffer time to time windows
- Dynamic rerouting during day
问题:
- 静态路线无法应对交通变化
- 持续配送延迟
解决方案:
- 使用时间依赖型行驶时间
- 整合历史交通数据
- 实时交通API(Google、HERE)
- 为时间窗增加缓冲时间
- 日间动态重路由
Challenge: Mixed Fleet
挑战:混合车队
Problem:
- Different vehicle types (trucks, vans, bikes)
- Different capacities and costs
Solutions:
- Model as heterogeneous fleet VRP
- Assign vehicle type per route
- Cost per mile by vehicle type
- Prioritize smaller/cheaper vehicles
- Consider vehicle-customer compatibility
问题:
- 不同车辆类型(卡车、货车、自行车)
- 不同容量和成本
解决方案:
- 建模为异构车队VRP
- 按路线分配车辆类型
- 按车辆类型计算每英里成本
- 优先使用小型/低成本车辆
- 考虑车辆与客户的兼容性
Output Format
输出格式
Route Optimization Report
路径优化报告
Executive Summary:
- Total routes: 12
- Total distance: 845 miles (15% reduction vs. current)
- Total time: 96 hours
- Vehicle utilization: 87% average
- On-time delivery: 98% (vs. 89% current)
Route Details:
| Route | Driver | Stops | Distance | Duration | Load | Utilization | Start Time |
|---|---|---|---|---|---|---|---|
| R1 | Smith | 18 | 72 mi | 8.5 hrs | 1,850 lbs | 93% | 7:00 AM |
| R2 | Jones | 15 | 65 mi | 7.8 hrs | 1,720 lbs | 86% | 7:00 AM |
| R3 | Brown | 22 | 83 mi | 9.2 hrs | 1,980 lbs | 99% | 6:30 AM |
Stop Sequence (Example Route R1):
- Depot - 7:00 AM
- Customer A (123 Main St) - 7:25 AM (ETA: 7:20-7:40)
- Customer B (456 Oak Ave) - 7:52 AM (ETA: 7:45-8:00)
- Customer C (789 Elm St) - 8:18 AM (ETA: 8:00-8:30) ...
- Customer R (321 Pine Rd) - 3:45 PM (ETA: 3:30-4:00)
- Return to Depot - 4:15 PM
Performance Metrics:
| Metric | Current | Optimized | Improvement |
|---|---|---|---|
| Total miles | 995 | 845 | -15% |
| Labor hours | 112 | 96 | -14% |
| Vehicles used | 14 | 12 | -14% |
| Avg stops/route | 15 | 18 | +20% |
| Fuel cost | $1,990 | $1,690 | -15% |
| Late deliveries | 11% | 2% | -82% |
Recommendations:
- Implement route optimization software
- Provide mobile apps to drivers
- Establish dynamic re-optimization process
- Monitor and adjust time windows
- Consider adding 1 vehicle for peak days
执行摘要:
- 总路线数:12条
- 总行驶距离:845英里(较当前减少15%)
- 总时长:96小时
- 车辆平均利用率:87%
- 准时配送率:98%(当前为89%)
路线详情:
| 路线 | 司机 | 站点数 | 行驶距离 | 时长 | 负载 | 利用率 | 出发时间 |
|---|---|---|---|---|---|---|---|
| R1 | Smith | 18 | 72英里 | 8.5小时 | 1,850磅 | 93% | 7:00 AM |
| R2 | Jones | 15 | 65英里 | 7.8小时 | 1,720磅 | 86% | 7:00 AM |
| R3 | Brown | 22 | 83英里 | 9.2小时 | 1,980磅 | 99% | 6:30 AM |
站点序列(示例路线R1):
- 仓库 - 7:00 AM
- 客户A(主街123号) - 7:25 AM(预计到达时间:7:20-7:40)
- 客户B(橡树大道456号) - 7:52 AM(预计到达时间:7:45-8:00)
- 客户C(榆树街789号) - 8:18 AM(预计到达时间:8:00-8:30) ...
- 客户R(松树路321号) - 3:45 PM(预计到达时间:3:30-4:00)
- 返回仓库 - 4:15 PM
绩效指标:
| 指标 | 当前值 | 优化后 | 提升幅度 |
|---|---|---|---|
| 总里程 | 995英里 | 845英里 | -15% |
| 工时 | 112小时 | 96小时 | -14% |
| 使用车辆数 | 14辆 | 12辆 | -14% |
| 平均每路线站点数 | 15个 | 18个 | +20% |
| 燃油成本 | $1,990 | $1,690 | -15% |
| 延迟配送率 | 11% | 2% | -82% |
建议:
- 部署路径优化软件
- 为司机提供移动端应用
- 建立动态重优化流程
- 监控并调整时间窗
- 考虑在高峰时段增加1辆车辆
Questions to Ask
需要询问的问题
If you need more context:
- How many stops per day? How many vehicles?
- Are there delivery time windows? Hard or soft constraints?
- What are the vehicle capacity limits?
- Single depot or multiple depots?
- Any special constraints (driver shifts, access restrictions)?
- Do you need pickups, deliveries, or both?
- Real-time changes needed or static planning?
- What's the current routing process?
如果需要更多上下文信息:
- 每日站点数量?车辆数量?
- 是否有配送时间窗?是硬性还是软性约束?
- 车辆容量限制是什么?
- 是单仓库还是多仓库?
- 是否有特殊约束(司机班次、通行限制)?
- 需要处理取货、配送,还是两者都有?
- 需要实时变化处理还是静态规划?
- 当前的路径规划流程是什么?
Related Skills
相关技能
- vehicle-routing-problem: Mathematical VRP models and variants
- fleet-management: Vehicle sizing, maintenance, and fleet operations
- last-mile-delivery: Urban delivery specific optimization
- network-design: Depot location and network structure
- freight-optimization: Long-haul transportation optimization
- capacitated-vrp: Capacity-focused VRP modeling
- vrp-time-windows: Time window constraint modeling
- traveling-salesman-problem: Single-vehicle routing
- pickup-delivery-problem: Pickup and delivery routing
- vehicle-routing-problem: VRP数学模型及变体
- fleet-management: 车辆规模规划、维护及车队运营
- last-mile-delivery: 城市配送特定优化
- network-design: 仓库选址与网络结构
- freight-optimization: 长途运输优化
- capacitated-vrp: 带容量约束的VRP建模
- vrp-time-windows: 带时间窗约束的VRP建模
- traveling-salesman-problem: 单车辆路径规划
- pickup-delivery-problem: 取货与配送路径规划