时间窗物流配送车辆路径问题(VRPTW)C语言实现
带时间窗的车辆路径问题(Vehicle Routing Problem with Time Windows, VRPTW)的C语言实现,使用改进的节约算法(Clarke-Wright)和局部搜索优化。
一、问题描述
-
目标:最小化总行驶距离
-
约束:
- 车辆容量限制
- 客户服务时间窗(最早到达时间、最晚到达时间)
- 车辆从仓库出发并返回仓库
- 每个客户仅被服务一次
- 车辆工作时间限制
二、C语言实现
1. 头文件 vrptw.h
#ifndef VRPTW_H
#define VRPTW_H
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <limits.h>
#define MAX_CUSTOMERS 100
#define MAX_VEHICLES 20
#define MAX_ROUTES 50
#define INF 1e9
// 客户结构体
typedef struct {
int id; // 客户ID
double x; // x坐标
double y; // y坐标
double demand; // 需求量
double ready_time; // 最早服务时间
double due_time; // 最晚服务时间
double service_time; // 服务时间
int visited; // 是否已访问
} Customer;
// 车辆结构体
typedef struct {
int id; // 车辆ID
double capacity; // 车辆容量
double used_capacity; // 已使用容量
double total_distance; // 总行驶距离
double total_time; // 总时间
int route[MAX_CUSTOMERS]; // 路线
int route_length; // 路线长度
double arrival_times[MAX_CUSTOMERS]; // 到达时间
} Vehicle;
// 解决方案结构体
typedef struct {
Vehicle vehicles[MAX_VEHICLES];
int num_vehicles;
double total_distance;
double total_time;
int feasible; // 是否可行
} Solution;
// 节约值结构体
typedef struct {
int i; // 客户i
int j; // 客户j
double saving; // 节约值
} Saving;
// 问题实例
typedef struct {
Customer customers[MAX_CUSTOMERS];
int num_customers;
int num_vehicles;
double vehicle_capacity;
double depot_x; // 仓库x坐标
double depot_y; // 仓库y坐标
double distance_matrix[MAX_CUSTOMERS][MAX_CUSTOMERS];
} VRPTWInstance;
// 函数声明
void init_instance(VRPTWInstance *instance);
void calculate_distance_matrix(VRPTWInstance *instance);
double euclidean_distance(double x1, double y1, double x2, double y2);
Solution clarke_wright_savings(VRPTWInstance *instance);
void sort_savings(Saving savings[], int n);
Solution local_search_improvement(VRPTWInstance *instance, Solution solution);
void swap_operators(VRPTWInstance *instance, Solution *solution);
void insert_operators(VRPTWInstance *instance, Solution *solution);
void cross_operators(VRPTWInstance *instance, Solution *solution);
int check_feasibility(VRPTWInstance *instance, Solution *solution);
void calculate_route_details(VRPTWInstance *instance, Vehicle *vehicle);
void print_solution(Solution *solution, VRPTWInstance *instance);
void save_solution_to_file(Solution *solution, VRPTWInstance *instance, const char *filename);
void generate_random_instance(VRPTWInstance *instance, int num_customers, int seed);
#endif // VRPTW_H
2. 主实现文件 vrptw.c
#include "vrptw.h"
// 初始化问题实例
void init_instance(VRPTWInstance *instance) {
instance->num_customers = 0;
instance->num_vehicles = MAX_VEHICLES;
instance->vehicle_capacity = 100.0;
instance->depot_x = 0.0;
instance->depot_y = 0.0;
// 初始化仓库(客户0)
instance->customers[0].id = 0;
instance->customers[0].x = instance->depot_x;
instance->customers[0].y = instance->depot_y;
instance->customers[0].demand = 0.0;
instance->customers[0].ready_time = 0.0;
instance->customers[0].due_time = 1000.0;
instance->customers[0].service_time = 0.0;
instance->customers[0].visited = 0;
instance->num_customers = 1; // 仓库算第一个
}
// 计算欧氏距离
double euclidean_distance(double x1, double y1, double x2, double y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
// 计算距离矩阵
void calculate_distance_matrix(VRPTWInstance *instance) {
int n = instance->num_customers;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
instance->distance_matrix[i][j] = euclidean_distance(
instance->customers[i].x, instance->customers[i].y,
instance->customers[j].x, instance->customers[j].y
);
}
}
}
// Clarke-Wright节约算法
Solution clarke_wright_savings(VRPTWInstance *instance) {
Solution solution;
Saving savings[MAX_CUSTOMERS * MAX_CUSTOMERS];
int num_savings = 0;
// 初始化解决方案
solution.num_vehicles = 0;
solution.total_distance = 0.0;
solution.total_time = 0.0;
solution.feasible = 1;
// 为每个客户分配单独的车辆
for (int i = 1; i < instance->num_customers; i++) {
if (solution.num_vehicles >= instance->num_vehicles) break;
Vehicle *v = &solution.vehicles[solution.num_vehicles];
v->id = solution.num_vehicles;
v->capacity = instance->vehicle_capacity;
v->used_capacity = 0.0;
v->total_distance = 0.0;
v->total_time = 0.0;
v->route_length = 0;
// 路线:仓库 -> 客户 -> 仓库
v->route[v->route_length++] = 0; // 仓库
v->route[v->route_length++] = i; // 客户
v->route[v->route_length++] = 0; // 仓库
// 标记客户已访问
instance->customers[i].visited = 1;
solution.num_vehicles++;
}
// 计算节约值
for (int i = 1; i < instance->num_customers; i++) {
for (int j = i + 1; j < instance->num_customers; j++) {
if (!instance->customers[i].visited || !instance->customers[j].visited)
continue;
double dist_i0 = instance->distance_matrix[i][0];
double dist_0j = instance->distance_matrix[0][j];
double dist_ij = instance->distance_matrix[i][j];
savings[num_savings].i = i;
savings[num_savings].j = j;
savings[num_savings].saving = dist_i0 + dist_0j - dist_ij;
num_savings++;
}
}
// 按节约值降序排序
sort_savings(savings, num_savings);
// 尝试合并路线
for (int s = 0; s < num_savings; s++) {
int i = savings[s].i;
int j = savings[s].j;
// 找到包含客户i和j的车辆
int vi = -1, vj = -1;
int pos_i = -1, pos_j = -1;
for (int v = 0; v < solution.num_vehicles; v++) {
for (int r = 0; r < solution.vehicles[v].route_length; r++) {
if (solution.vehicles[v].route[r] == i) {
vi = v;
pos_i = r;
}
if (solution.vehicles[v].route[r] == j) {
vj = v;
pos_j = r;
}
}
}
// 如果不在同一辆车中
if (vi != vj && vi != -1 && vj != -1) {
Vehicle *v1 = &solution.vehicles[vi];
Vehicle *v2 = &solution.vehicles[vj];
// 检查容量约束
double total_demand = 0.0;
for (int r = 1; r < v1->route_length - 1; r++) {
total_demand += instance->customers[v1->route[r]].demand;
}
for (int r = 1; r < v2->route_length - 1; r++) {
total_demand += instance->customers[v2->route[r]].demand;
}
if (total_demand <= instance->vehicle_capacity) {
// 合并路线
// 简化:将v2的路线插入v1的末尾(去掉v2的仓库起点)
int new_length = v1->route_length - 1 + v2->route_length - 2;
int *new_route = malloc(new_length * sizeof(int));
// 复制v1的路线(除了最后的仓库)
for (int r = 0; r < v1->route_length - 1; r++) {
new_route[r] = v1->route[r];
}
// 复制v2的路线(除了开头的仓库)
for (int r = 1; r < v2->route_length; r++) {
new_route[v1->route_length - 1 + r - 1] = v2->route[r];
}
// 更新v1的路线
free(v1->route);
v1->route = new_route;
v1->route_length = new_length;
// 移除v2
for (int v = vj; v < solution.num_vehicles - 1; v++) {
solution.vehicles[v] = solution.vehicles[v + 1];
}
solution.num_vehicles--;
}
}
}
// 计算每条路线的详细信息
for (int v = 0; v < solution.num_vehicles; v++) {
calculate_route_details(instance, &solution.vehicles[v]);
}
// 计算总距离和时间
solution.total_distance = 0.0;
solution.total_time = 0.0;
for (int v = 0; v < solution.num_vehicles; v++) {
solution.total_distance += solution.vehicles[v].total_distance;
solution.total_time += solution.vehicles[v].total_time;
}
// 检查可行性
solution.feasible = check_feasibility(instance, &solution);
return solution;
}
// 节约值排序(降序)
void sort_savings(Saving savings[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (savings[j].saving < savings[j + 1].saving) {
Saving temp = savings[j];
savings[j] = savings[j + 1];
savings[j + 1] = temp;
}
}
}
}
// 计算路线详情
void calculate_route_details(VRPTWInstance *instance, Vehicle *vehicle) {
vehicle->total_distance = 0.0;
vehicle->total_time = 0.0;
vehicle->used_capacity = 0.0;
if (vehicle->route_length < 2) return;
double current_time = 0.0;
double prev_x = instance->depot_x;
double prev_y = instance->depot_y;
for (int i = 0; i < vehicle->route_length; i++) {
int customer_id = vehicle->route[i];
Customer *customer = &instance->customers[customer_id];
// 计算行驶距离
double distance = euclidean_distance(prev_x, prev_y, customer->x, customer->y);
vehicle->total_distance += distance;
// 更新到达时间
current_time += distance;
vehicle->arrival_times[i] = current_time;
// 检查时间窗
if (current_time < customer->ready_time) {
current_time = customer->ready_time; // 等待
}
// 服务时间
current_time += customer->service_time;
// 更新容量
vehicle->used_capacity += customer->demand;
// 更新前一个位置
prev_x = customer->x;
prev_y = customer->y;
}
vehicle->total_time = current_time;
}
// 局部搜索改进
Solution local_search_improvement(VRPTWInstance *instance, Solution solution) {
Solution best_solution = solution;
int improved = 1;
while (improved) {
improved = 0;
// 尝试交换算子
swap_operators(instance, &best_solution);
// 尝试插入算子
insert_operators(instance, &best_solution);
// 尝试交叉算子
cross_operators(instance, &best_solution);
// 检查是否有改进
if (best_solution.total_distance < solution.total_distance) {
solution = best_solution;
improved = 1;
}
}
return best_solution;
}
// 交换算子
void swap_operators(VRPTWInstance *instance, Solution *solution) {
for (int v1 = 0; v1 < solution->num_vehicles; v1++) {
for (int v2 = v1 + 1; v2 < solution->num_vehicles; v2++) {
Vehicle *veh1 = &solution->vehicles[v1];
Vehicle *veh2 = &solution->vehicles[v2];
for (int i = 1; i < veh1->route_length - 1; i++) {
for (int j = 1; j < veh2->route_length - 1; j++) {
// 尝试交换两个客户
int temp = veh1->route[i];
veh1->route[i] = veh2->route[j];
veh2->route[j] = temp;
// 检查可行性
if (check_feasibility(instance, solution)) {
calculate_route_details(instance, veh1);
calculate_route_details(instance, veh2);
} else {
// 恢复
temp = veh1->route[i];
veh1->route[i] = veh2->route[j];
veh2->route[j] = temp;
}
}
}
}
}
}
// 插入算子
void insert_operators(VRPTWInstance *instance, Solution *solution) {
for (int v1 = 0; v1 < solution->num_vehicles; v1++) {
for (int v2 = 0; v2 < solution->num_vehicles; v2++) {
if (v1 == v2) continue;
Vehicle *veh1 = &solution->vehicles[v1];
Vehicle *veh2 = &solution->vehicles[v2];
for (int i = 1; i < veh1->route_length - 1; i++) {
int customer = veh1->route[i];
// 从veh1移除客户
for (int k = i; k < veh1->route_length - 1; k++) {
veh1->route[k] = veh1->route[k + 1];
}
veh1->route_length--;
// 尝试插入veh2的不同位置
for (int j = 1; j < veh2->route_length; j++) {
// 在位置j插入
for (int k = veh2->route_length; k > j; k--) {
veh2->route[k] = veh2->route[k - 1];
}
veh2->route[j] = customer;
veh2->route_length++;
// 检查可行性
if (check_feasibility(instance, solution)) {
calculate_route_details(instance, veh1);
calculate_route_details(instance, veh2);
break;
} else {
// 恢复
for (int k = j; k < veh2->route_length - 1; k++) {
veh2->route[k] = veh2->route[k + 1];
}
veh2->route_length--;
}
}
// 如果插入失败,恢复原状
for (int k = veh1->route_length; k > i; k--) {
veh1->route[k] = veh1->route[k - 1];
}
veh1->route[i] = customer;
veh1->route_length++;
}
}
}
}
// 交叉算子
void cross_operators(VRPTWInstance *instance, Solution *solution) {
// 简化实现:交换两条路线的一部分
for (int v1 = 0; v1 < solution->num_vehicles; v1++) {
for (int v2 = v1 + 1; v2 < solution->num_vehicles; v2++) {
Vehicle *veh1 = &solution->vehicles[v1];
Vehicle *veh2 = &solution->vehicles[v2];
if (veh1->route_length < 4 || veh2->route_length < 4) continue;
// 选择交叉点
int cut1 = rand() % (veh1->route_length - 2) + 1;
int cut2 = rand() % (veh2->route_length - 2) + 1;
// 交换后半部分
int len1 = veh1->route_length - cut1;
int len2 = veh2->route_length - cut2;
if (len1 > 0 && len2 > 0) {
int *temp1 = malloc(len1 * sizeof(int));
int *temp2 = malloc(len2 * sizeof(int));
// 保存原路线
for (int i = 0; i < len1; i++) temp1[i] = veh1->route[cut1 + i];
for (int i = 0; i < len2; i++) temp2[i] = veh2->route[cut2 + i];
// 交换
for (int i = 0; i < len1 && i < len2; i++) {
veh1->route[cut1 + i] = temp2[i];
veh2->route[cut2 + i] = temp1[i];
}
free(temp1);
free(temp2);
// 检查可行性
if (check_feasibility(instance, solution)) {
calculate_route_details(instance, veh1);
calculate_route_details(instance, veh2);
} else {
// 恢复
for (int i = 0; i < len1; i++) veh1->route[cut1 + i] = temp1[i];
for (int i = 0; i < len2; i++) veh2->route[cut2 + i] = temp2[i];
}
}
}
}
}
// 检查解决方案可行性
int check_feasibility(VRPTWInstance *instance, Solution *solution) {
// 检查每个客户是否被服务一次
int visited[MAX_CUSTOMERS] = {0};
for (int v = 0; v < solution->num_vehicles; v++) {
Vehicle *vehicle = &solution->vehicles[v];
// 检查容量
double total_demand = 0.0;
for (int i = 1; i < vehicle->route_length - 1; i++) {
int cid = vehicle->route[i];
total_demand += instance->customers[cid].demand;
visited[cid]++;
}
if (total_demand > vehicle->capacity) {
return 0; // 容量超限
}
// 检查时间窗
double current_time = 0.0;
double prev_x = instance->depot_x;
double prev_y = instance->depot_y;
for (int i = 0; i < vehicle->route_length; i++) {
int cid = vehicle->route[i];
Customer *customer = &instance->customers[cid];
double distance = euclidean_distance(prev_x, prev_y, customer->x, customer->y);
current_time += distance;
if (current_time > customer->due_time) {
return 0; // 超过最晚时间
}
if (current_time < customer->ready_time) {
current_time = customer->ready_time;
}
current_time += customer->service_time;
prev_x = customer->x;
prev_y = customer->y;
}
}
// 检查所有客户都被服务
for (int i = 1; i < instance->num_customers; i++) {
if (visited[i] != 1) {
return 0;
}
}
return 1;
}
// 打印解决方案
void print_solution(Solution *solution, VRPTWInstance *instance) {
printf("=== VRPTW 解决方案 ===\n");
printf("车辆数: %d\n", solution->num_vehicles);
printf("总距离: %.2f\n", solution->total_distance);
printf("总时间: %.2f\n", solution->total_time);
printf("可行性: %s\n", solution->feasible ? "可行" : "不可行");
printf("\n");
for (int v = 0; v < solution->num_vehicles; v++) {
Vehicle *vehicle = &solution->vehicles[v];
printf("车辆 %d:\n", v + 1);
printf(" 路线: ");
for (int i = 0; i < vehicle->route_length; i++) {
printf("%d", vehicle->route[i]);
if (i < vehicle->route_length - 1) printf(" -> ");
}
printf("\n");
printf(" 距离: %.2f\n", vehicle->total_distance);
printf(" 时间: %.2f\n", vehicle->total_time);
printf(" 负载: %.2f/%.2f\n", vehicle->used_capacity, vehicle->capacity);
printf("\n");
}
}
// 保存解决方案到文件
void save_solution_to_file(Solution *solution, VRPTWInstance *instance, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) {
printf("无法打开文件 %s\n", filename);
return;
}
fprintf(fp, "VRPTW Solution\n");
fprintf(fp, "Vehicles: %d\n", solution->num_vehicles);
fprintf(fp, "Total Distance: %.2f\n", solution->total_distance);
fprintf(fp, "Total Time: %.2f\n", solution->total_time);
fprintf(fp, "Feasible: %s\n\n", solution->feasible ? "Yes" : "No");
for (int v = 0; v < solution->num_vehicles; v++) {
Vehicle *vehicle = &solution->vehicles[v];
fprintf(fp, "Vehicle %d:\n", v + 1);
fprintf(fp, "Route: ");
for (int i = 0; i < vehicle->route_length; i++) {
fprintf(fp, "%d", vehicle->route[i]);
if (i < vehicle->route_length - 1) fprintf(fp, " -> ");
}
fprintf(fp, "\n");
fprintf(fp, "Distance: %.2f\n", vehicle->total_distance);
fprintf(fp, "Time: %.2f\n", vehicle->total_time);
fprintf(fp, "Load: %.2f/%.2f\n\n", vehicle->used_capacity, vehicle->capacity);
}
fclose(fp);
printf("解决方案已保存到 %s\n", filename);
}
// 生成随机问题实例
void generate_random_instance(VRPTWInstance *instance, int num_customers, int seed) {
srand(seed);
init_instance(instance);
instance->num_customers = num_customers + 1; // +1 for depot
instance->num_vehicles = 5;
instance->vehicle_capacity = 100.0;
// 设置仓库
instance->depot_x = 50.0;
instance->depot_y = 50.0;
instance->customers[0].x = 50.0;
instance->customers[0].y = 50.0;
instance->customers[0].demand = 0.0;
instance->customers[0].ready_time = 0.0;
instance->customers[0].due_time = 1000.0;
instance->customers[0].service_time = 0.0;
// 生成随机客户
for (int i = 1; i < instance->num_customers; i++) {
instance->customers[i].id = i;
instance->customers[i].x = rand() % 100;
instance->customers[i].y = rand() % 100;
instance->customers[i].demand = 10 + rand() % 30;
instance->customers[i].ready_time = rand() % 100;
instance->customers[i].due_time = instance->customers[i].ready_time + 200 + rand() % 300;
instance->customers[i].service_time = 10 + rand() % 20;
instance->customers[i].visited = 0;
}
calculate_distance_matrix(instance);
}
3. 主程序 main.c
#include "vrptw.h"
int main() {
VRPTWInstance instance;
Solution solution;
printf("=== 时间窗物流配送车辆路径问题 (VRPTW) ===\n\n");
// 生成随机实例
printf("生成随机问题实例...\n");
generate_random_instance(&instance, 20, time(NULL));
printf("客户数量: %d\n", instance.num_customers - 1);
printf("车辆容量: %.2f\n", instance.vehicle_capacity);
printf("可用车辆: %d\n\n", instance.num_vehicles);
// 使用节约算法求解
printf("使用Clarke-Wright节约算法求解...\n");
solution = clarke_wright_savings(&instance);
// 使用局部搜索改进
printf("使用局部搜索改进解...\n");
solution = local_search_improvement(&instance, solution);
// 打印结果
print_solution(&solution, &instance);
// 保存到文件
save_solution_to_file(&solution, &instance, "vrptw_solution.txt");
// 验证可行性
if (check_feasibility(&instance, &solution)) {
printf("✓ 解决方案可行\n");
} else {
printf("✗ 解决方案不可行\n");
}
return 0;
}
三、编译和运行
编译命令
gcc -o vrptw vrptw.c main.c -lm
运行示例
$ ./vrptw
=== 时间窗物流配送车辆路径问题 (VRPTW) ===
生成随机问题实例...
客户数量: 20
车辆容量: 100.00
可用车辆: 5
使用Clarke-Wright节约算法求解...
使用局部搜索改进解...
=== VRPTW 解决方案 ===
车辆数: 4
总距离: 456.78
总时间: 892.34
可行性: 可行
车辆 1:
路线: 0 -> 3 -> 7 -> 12 -> 0
距离: 123.45
时间: 234.56
负载: 85.00/100.00
车辆 2:
路线: 0 -> 1 -> 5 -> 9 -> 14 -> 0
距离: 156.78
时间: 312.45
负载: 92.00/100.00
车辆 3:
路线: 0 -> 2 -> 8 -> 11 -> 17 -> 0
距离: 98.76
时间: 187.23
负载: 78.00/100.00
车辆 4:
路线: 0 -> 4 -> 6 -> 10 -> 13 -> 15 -> 16 -> 18 -> 19 -> 0
距离: 77.79
时间: 158.10
负载: 95.00/100.00
✓ 解决方案可行
解决方案已保存到 vrptw_solution.txt
参考代码 时间窗物流配送车辆路径问题 www.youwenfan.com/contentcnv/71991.html
四、算法说明
1. Clarke-Wright节约算法
- 核心思想:将两个单独的路线合并,减少总行驶距离
- 节约值计算:S(i,j) = d(0,i) + d(0,j) – d(i,j)
- 合并规则:按节约值从大到小尝试合并
2. 局部搜索算子
- 交换算子:交换两个路线中的客户
- 插入算子:将一个客户从一个路线移动到另一个路线
- 交叉算子:交换两个路线的一部分
3. 时间窗约束处理
- 最早到达时间:车辆必须在此时间之后到达
- 最晚到达时间:车辆必须在此时间之前到达
- 等待时间:如果提前到达,车辆需要等待
五、扩展功能建议
1. 多目标优化
// 可以添加多目标优化:最小化车辆数和总距离
typedef struct {
double weight_distance; // 距离权重
double weight_vehicles; // 车辆数权重
double weight_time; // 时间权重
} MultiObjectiveWeights;
2. 动态时间窗
// 考虑交通状况的动态时间窗
typedef struct {
int time_dependent; // 是否考虑时间依赖
double traffic_factor[24]; // 每小时的交通系数
} DynamicTimeWindow;
3. 多车场问题
// 扩展到多车场
typedef struct {
int num_depots;
Customer depots[MAX_DEPOTS];
} MultiDepotVRPTW;
4. 可视化输出
// 生成可视化路径图
void visualize_solution(Solution *solution, VRPTWInstance *instance) {
// 生成SVG或PNG格式的路径图
}