Author: Camilo Bermúdez
This project addresses the Vehicle Routing Problem with Time Windows (VRPTW). The project implements and evaluates multiple heuristic and metaheuristic algorithms, including Constructive Heuristics, GRASP, Parallel Simulated Annealing, Parallel Variable Neighborhood Descent (VND), and a Genetic Algorithm (GA). Each method balances exploration and exploitation of the solution space to deliver high-quality, feasible routes. The algorithms are tested on benchmark instances derived from Solomon's VRPTW dataset, ranging from small instances with 25 customers to large-scale instances with 100 customers. Results demonstrate that the Parallel Simulated Annealing and the Genetic Algorithm achieve the most competitive solutions when prioritizing runtime and vehicle minimization, while the Parallel VND works better at reducing total distance at a higher computational cost. This work provides an organized codebase for testing various VRPTW heuristics, alongside detailed results, parameter sensitivity analysis, and runtime comparisons. Additionally, a presentation in Spanish offers further insights into the implementation and methodology of the algorithms.
This project implements several heuristic algorithms to solve the Vehicle Routing Problem with Time Windows (VRPTW). The VRPTW is a combinatorial optimization problem where a set of routes must be constructed for vehicles to deliver goods to customers, minimizing both the number of vehicles and the total travel distance while respecting vehicle capacity constraints and time window constraints for each customer.
The VRTPW has a wide range of real-world applications, particularly in logistics, transportation, and delivery services. Such as optimizing routes for delivery vehicles to ensure timely deliveries while minimizing the fleet size and travel distances, scheduling collection vehicles to service households or businesses within specific time windows, designing bus or shuttle routes to adhere to passenger schedules and reduce vehicle usage, planning efficient routes for mail and parcel delivery within predefined time windows, or managing the delivery of goods to warehouses or retail locations while meeting time-sensitive demands.
The implemented algorithms include:
- Constructive Algorithm: A deterministic greedy method for constructing routes.
- Constructive Algorithm with Noise: Adds controlled randomness to edge weights to diversify solutions.
- GRASP (Greedy Randomized Adaptive Search Procedure): Introduces probabilistic selection of edges during route construction.
- Parallel Simulated Annealing: A metaheuristic optimization method that explores solutions using parallel simulated annealing processes.
- Parallel Variable Neighborhood Descent: A local search method that systematically explores different neighborhoods to improve solutions.
- Genetic Algorithm: An evolutionary optimization method that applies selection, crossover, and mutation to evolve high-quality solutions.
Each algorithm is evaluated on its ability to minimize both the number of vehicles (primary metric) and the total distance while providing diverse and feasible solutions.
In the Vehicle Routing Problem with Time Windows (VRPTW):
-
Vehicles:
- Start and end at a depot (node
$0$ ). - Have a maximum capacity
$Q$ that cannot be exceeded.
- Start and end at a depot (node
-
Customer Nodes:
- Each node
$i$ has a demand$q_i$ . - Each node
$i$ has a time window$[e_i, l_i]$ , where$e_i$ is the earliest start time and$l_i$ is the latest time to begin service. - Each node
$i$ has a service time$s_i$ required to unload the goods. - Distance between nodes
$i$ and$j$ ($d_{ij}$ ). -
$\text{MaxDistance}_0$ : The maximum distance from the depot to any node.
- Each node
-
Objective:
- Minimize the number of vehicles used.
- Minimize the total travel distance, while ensuring all customers are serviced within their respective time windows.
For some of the algorithms, the problem is represented as a graph
The graph is built with edge weights defined based on the following formula:
Before adding an edge between nodes
- The vehicle must arrive at node
$j$ before its time window closes:$\max(d_{0i}, e_i) + s_i + d_{ij} \leq l_j$ . - The vehicle must return to the depot within its closing time window:
$\max(d_{0i}, e_i) + s_i + d_{ij} + s_j + d_{j0} \leq l_0$ . - The sum of the demands of nodes
$i$ and$j$ must not exceed the vehicle capacity:$q_i + q_j \leq Q$ .
-
$\alpha$ : Balances distance and time window readiness for non-depot nodes. -
$\beta$ : Adjusts the importance of servicing distant nodes early versus nodes with earlier time window closures. It is applied only to edges from the depot ($i = 0$ ). That way ensures that initial routes from the depot prioritize distant nodes or nodes with tight time windows, improving overall route structure. -
$\mu_{i,j}$ : Adds bias to edge weights based on relative proximity to the depot, so it adjusts edge weights to favor nodes that are farther from the depot and helps in constructing efficient routes for distant areas:
- Start at the depot (node
$0$ ). - At each step, select the best feasible neighbor based on sorted edge weights.
- Continue until all nodes are visited while respecting:
- Vehicle capacity constraints.
- Time window constraints.
- Return to the depot to complete the route.
The graph is constructed similarly to the Constructive Algorithm, but noise is added to the edge weights to introduce variability:
Noise is sampled from a uniform distribution based on
- If
$\mu_{i,j} > 0$ :$\text{r} \sim U(0, \mu_{i,j})$ - If
$\mu_{i,j} < 0$ :$\text{r} \sim U(\mu_{i,j}, 0)$ - If
$\mu_{i,j} = 0$ , no noise is added.
- The routes are built using the same process as in the Constructive Algorithm.
- The noise introduces variability, leading to different solutions across runs.
The graph is constructed using the same weight formula as in the Constructive Algorithm, including the role of
- Start at the depot (node
$0$ ). - Build a list of feasible neighbors based on constraints:
- Time window constraints.
- Vehicle capacity.
- Compute edge weights for the neighbors, influenced by
$\alpha$ ,$\beta$ , and$\mu_{i,j}$ . - Select a neighbor probabilistically using a geometric distribution:
$P(k) = (1 - p)^{k - 1} p, \quad k \geq 1$ , where$k$ is the index of the selected neighbor in the sorted list. - If the generated index exceeds the candidate list size, repeat the selection.
- Continue until all nodes are visited and return to the depot.
The geometric distribution skews the selection towards better candidates (lower
The Parallel Simulated Annealing (Parallel SA) algorithm extends the classical Simulated Annealing by allowing multiple threads to explore solutions independently while periodically exchanging solutions to enhance the overall search process.
Each thread starts with an initial solution generated using the Constructive Algorithm with Noise. The temperature for each thread is initialized as
Each thread explores the solution space using Simulated Annealing. At each iteration:
-
A new solution is generated by perturbing the current solution using one of the following neighborhood operators:
- Shift: Move a node to a different position or route.
- Rearrange: Reorder nodes within the same route.
- Exchange: Swap nodes or segments between two routes.
- Reverse: Reverse the order of a segment within a route.
-
The cost of the new solution is evaluated. If it improves the current solution in the thread, it is accepted. If the new solution is worse, it is accepted with a probability
$\text{P} = \frac{T + \delta}{T}$ , where$T$ is the current temperature and$\delta$ is the cost difference between the new solution and the current solution. A random number between 0 and 1 determines whether the solution is accepted. -
The temperature is gradually decreased (
$T_{i+1} = \beta T_i$ ) during the search to reduce the acceptance of worse solutions and focus on exploitation.
Every
If the equilibrium counter reaches a threshold
The Parallel Variable Neighborhood Descent algorithm extends the classical VND by distributing the search process across multiple threads, where each thread explores the solution space independently while systematically applying neighborhood structures.
Each thread starts with an initial solution generated using the Constructive Algorithm with Noise. Threads iteratively improve their solutions by exploring predefined neighborhoods in a structured order.
At each iteration:
-
A neighborhood operator is applied to perturb the current solution. The operators are systematically applied in the following order:
- Shift: Move a node to a different position or route.
- Rearrange: Reorder a sequence of nodes within a single route.
- Exchange: Swap nodes or segments between two routes.
- Swap: Swap single nodes between two routes.
- Reverse: Reverse the order of a segment within a route.
-
The perturbation step is followed by a local improvement phase using the
first_local_search
function. This function refines the perturbed solution to ensure that local improvements are applied before moving to the next neighborhood. -
If the new solution improves the current solution, the search restarts from the first neighborhood. If no improvement is found, the search progresses to the next neighborhood in the sequence.
-
Each thread explores its assigned neighborhoods independently. Threads keep track of their local best solutions during this process.
Periodically, the global best solution is updated based on the best local solutions found by all threads. This ensures that the search process benefits from the best solutions discovered across all threads.The algorithm terminates when all threads have explored their neighborhoods without finding further improvements.
Each solution, referred to as a chromosome, is represented as a sequence of customer nodes, which is a permutation of all customers. Routes are not explicitly defined in the chromosome. Instead, during the decoding phase, valid routes are constructed starting from the depot, ensuring all constraints are satisfied. The initial population consists of a mix of random and greedy solutions. Random solutions are generated by permuting the list of customers, while greedy solutions are constructed by starting from a random customer and iteratively adding the nearest unvisited customer within a predefined radius.
To decode a chromosome into a feasible VRPTW solution, the following process is applied:
- Customers are sequentially added to a route while checking vehicle capacity and time window feasibility
- After all routes are constructed, the algorithm attempts to move the last customer of each route to the beginning of the next route. This reinsertion is performed only if it improves the solution by reducing the objective function (number of vehicles or total distance) and respecting all constraints.
The fitness of each solution is evaluated based on the two objectives (number of vehicles and total distance). To handle these objectives simultaneously, Pareto Ranking is used. A solution is said to dominate another if it is no worse in all objectives and strictly better in at least one.
At each generation, the algorithm evolves the population through selection, crossover, and mutation.
The selection process is used to choose two parents for crossover. For each parent:
- Four individuals are randomly selected from the population.
- Among these four, the best solution is identified based on its Pareto rank.
- With a probability r (where r > 0.5), the best individual is selected.
- If the best individual is not selected, one of the other three individuals is chosen randomly.
This process ensures that solutions with better fitness have a higher chance of being selected, but diversity is maintained by occasionally selecting other individuals.
The crossover operator combines genetic material from two parents to create an offspring. It works as follows:
- A random route (a sequence of customer nodes) is selected from Parent 1.
- The nodes in the selected route are removed from Parent 2 to prevent duplicate visits.
- The removed nodes are reinserted into the offspring at positions that minimize the objective function (number of vehicles and total distance). Nodes are reinserted one by one, ensuring that vehicle capacity and time window constraints are satisfied.
The offspring retains structural characteristics from both parents while ensuring feasibility and improving solution quality.
Mutation introduces diversity into the population by altering solutions slightly. For each individual, a mutation is applied with a predefined probability. The mutation involves reversing a segment of two or three consecutive nodes within a route. The mutation is only accepted if the resulting solution respects constraints and improves the solution.
At the end of each generation elitism is applied to preserve the best solutions (Pareto rank 1) in the population. This guarantees that the best solutions discovered so far are not lost, then, parents are selected using tournament selection, offspring are generated through crossover and mutated to introduce diversity, and finally the population is replaced with the newly generated individuals, maintaining the population size. The algorithm evolves the population for a predefined number of generations and the best solutions are identified as the non-dominated solutions in the final population.
The instances used in this project come from the Solomon Benchmark for the Vehicle Routing Problem with Time Windows (VRPTW). Specifically:
- Instances VRPTW1, VRPTW7, and VRPTW13 correspond to the C101 instance from Solomon's benchmark, with 25, 50, and 100 customers, respectively.
- Instances VRPTW2, VRPTW8, and VRPTW14 correspond to the C201 instance with 25, 50, and 100 customers.
- Instances VRPTW3, VRPTW9, and VRPTW15 correspond to the R101 instance with 25, 50, and 100 customers.
- Instances VRPTW4, VRPTW10, and VRPTW16 correspond to the R201 instance with 25, 50, and 100 customers.
- Instances VRPTW5, VRPTW11, and VRPTW17 correspond to the RC101 instance with 25, 50, and 100 customers.
- Instances VRPTW6, VRPTW12, and VRPTW18 correspond to the RC201 instance with 25, 50, and 100 customers.
The last six instances (RPTW13 to VRPTW18) correspond to the original Solomon instances with 100 customers, which are available at Solomon Benchmark Instances.
These benchmark instances are widely used to evaluate VRPTW algorithms due to their standardized format and varying levels of complexity (clustered, random, and mixed customer distributions).
Instance | Constructive | Constructive with Noise | GRASP | Parallel SA | Parallel VND | GA |
---|---|---|---|---|---|---|
VRPTW1 | 3 | 3 | 3 | 3 | 3 | 3 |
VRPTW2 | 2 | 2 | 3 | 2 | 2 | 2 |
VRPTW3 | 8 | 8 | 8 | 8 | 8 | 8 |
VRPTW4 | 2 | 2 | 2 | 2 | 2 | 2 |
VRPTW5 | 4 | 4 | 4 | 4 | 5 | 4 |
VRPTW6 | 2 | 2 | 2 | 2 | 2 | 2 |
VRPTW7 | 5 | 5 | 6 | 5 | 5 | 5 |
VRPTW8 | 2 | 2 | 2 | 2 | 2 | 2 |
VRPTW9 | 12 | 12 | 12 | 11 | 12 | 11 |
VRPTW10 | 3 | 3 | 3 | 2 | 3 | 2 |
VRPTW11 | 9 | 8 | 9 | 8 | 9 | 8 |
VRPTW12 | 3 | 3 | 3 | 3 | 3 | 3 |
VRPTW13 | 10 | 10 | 12 | 10 | 10 | 10 |
VRPTW14 | 3 | 3 | 4 | 3 | 3 | 3 |
VRPTW15 | 21 | 20 | 20 | 19 | 20 | 19 |
VRPTW16 | 5 | 5 | 4 | 4 | 4 | 4 |
VRPTW17 | 17 | 16 | 16 | 16 | 17 | 15 |
VRPTW18 | 5 | 4 | 5 | 4 | 4 | 4 |
Average | 6.444 | 6.222 | 6.556 | 6.000 | 6.333 | 5.944 |
Instance | Constructive | Constructive with Noise | GRASP | Parallel SA | Parallel VND | GA |
---|---|---|---|---|---|---|
VRPTW1 | 191.815 | 191.815 | 191.815 | 191.815 | 191.815 | 191.815 |
VRPTW2 | 215.542 | 215.542 | 292.776 | 215.542 | 215.542 | 228.185 |
VRPTW3 | 660.155 | 660.155 | 630.379 | 618.328 | 618.328 | 624.633 |
VRPTW4 | 719.561 | 647.436 | 577.368 | 524.590 | 524.590 | 523.655 |
VRPTW5 | 488.446 | 470.616 | 470.951 | 462.153 | 476.953 | 462.153 |
VRPTW6 | 572.386 | 531.343 | 461.359 | 432.295 | 432.295 | 434.226 |
VRPTW7 | 363.248 | 363.248 | 440.698 | 363.248 | 363.248 | 363.248 |
VRPTW8 | 497.174 | 497.174 | 444.959 | 444.959 | 444.959 | 444.959 |
VRPTW9 | 1133.41 | 1124.37 | 1088.08 | 1100.72 | 1046.70 | 1108.86 |
VRPTW10 | 1179.64 | 1150.46 | 1065.58 | 1019.10 | 871.628 | 971.184 |
VRPTW11 | 1059.97 | 988.526 | 993.748 | 951.738 | 958.832 | 946.457 |
VRPTW12 | 1333.70 | 1199.92 | 1041.56 | 852.983 | 852.983 | 842.965 |
VRPTW13 | 855.065 | 855.065 | 1042.74 | 828.937 | 828.937 | 828.937 |
VRPTW14 | 591.555 | 591.555 | 622.868 | 591.555 | 591.555 | 591.555 |
VRPTW15 | 1953.76 | 1815.71 | 1880.76 | 1678.68 | 1654.63 | 1723.34 |
VRPTW16 | 1599.72 | 1533.43 | 1696.01 | 1337.87 | 1337.87 | 1312.40 |
VRPTW17 | 1879.78 | 1876.06 | 1805.32 | 1700.27 | 1691.02 | 1715.05 |
VRPTW18 | 1707.24 | 1699.14 | 1583.35 | 1504.79 | 1504.79 | 1534.71 |
Average | 944.565 | 911.754 | 907.240 | 823.310 | 811.482 | 824.907 |
Instance | Best Vehicles (Literature) | Best Vehicles (Methods) | Best Distance (Literature) | Best Distance (Methods) |
---|---|---|---|---|
VRPTW13 | 10 | 10 (SA, VNS, GA) | 828.94 | 828.94 (SA, VND, GA) |
VRPTW14 | 3 | 3 (All Methods) | 591.56 | 591.55 (All Methods) |
VRPTW15 | 19 | 19 (SA, GA) | 1650.80 | 1654.63 (VND) |
VRPTW16 | 4 | 4 (SA, VNS, GA) | 1252.37 | 1337.87 (SA, VND) |
VRPTW17 | 14 | 15 (GA) | 1696.95 | 1691.02 (VNS) |
VRPTW18 | 4 | 4 (SA, VNS, GA) | 1406.94 | 1504.79 (SA, VND) |
The performance of the methods is evaluated based on two primary metrics: the number of vehicles (the primary objective) and the total travel distance, while also considering computational efficiency to understand scalability across different instance sizes.
The Constructive Heuristic with Noise demonstrated sensitivity to the parameters
In the Simulated Annealing, the initial temperature
The Genetic Algorithm depended heavily on the tournament selection probability
The Constructive Heuristic provided the fastest and simplest solutions but consistently produced the highest number of vehicles and the longest total distances. It serves as an effective baseline for comparison due to its speed and computational efficiency.
By introducing variability during route construction, the Constructive Heuristic with Noise improved upon the baseline method. It achieved better average vehicle counts than the Parallel VND while maintaining the same runtime efficiency as the deterministic heuristic.
GRASP, while offering a good trade-off between exploration and runtime, performed slightly worse in terms of vehicle counts compared to the Constructive Heuristic with Noise. Nevertheless, its runtime remained competitive with the constructives, making it a practical choice for quickly generating feasible solutions.
The Parallel Simulated Annealing (SA) consistently delivered high-quality solutions, achieving competitive results for both vehicle counts and total distances. It significantly outperformed GRASP and the Constructive methods in solution quality while maintaining reasonable runtimes. The cooperation mechanism between threads enhanced convergence without adding substantial overhead.
Parallel Variable Neighborhood Descent (VND) produced the shortest total distances among all methods but underperformed in terms of the primary metric—the number of vehicles. Despite its strong results for distance minimization, it was the slowest algorithm overall, making it less suitable when computational efficiency is a concern.
Finally, the Genetic Algorithm (GA) emerged as the fastest metaheuristic, balancing runtime and solution quality effectively. It delivered competitive results for both metrics, offering a scalable and efficient solution for large instances. The crossover operator played a significant role in maintaining solution quality, while tournament selection ensured effective exploration of the solution space.
The Parallel Simulated Annealing (SA) and the Genetic Algorithm (GA) stood out as the most effective methods overall. Both achieved competitive results in minimizing the number of vehicles—the primary objective—while maintaining acceptable runtimes. Parallel SA consistently delivered high-quality solutions through its exploration-exploitation balance, while GA excelled in speed and scalability, making it a practical choice for large-scale instances.
Parallel Variable Neighborhood Descent (VND) demonstrated its strength in minimizing total travel distance, achieving the best results for this metric. However, it underperformed in the primary objective of minimizing vehicles and was computationally the slowest, making it less favorable when runtime is critical.
The Constructive Heuristics (deterministic and noise-enhanced) served as fast baseline methods, offering quick solutions with reasonable quality. The Constructive Heuristic with Noise, in particular, outperformed GRASP and Parallel VND in vehicle count while maintaining comparable runtimes to the basic heuristic.
Ultimately, the choice of method depends on the specific requirements of the problem. For scenarios where runtime is critical, the Genetic Algorithm or Constructive Heuristics are preferred. For applications prioritizing solution quality, especially total distance, Parallel SA and GA offer robust solutions at the cost of additional computational time.
To set up and compile the project, ensure the following dependencies are installed:
- C++ Compiler: A standard C++ compiler (e.g., g++ or MSVC).
- OpenMP: Required for parallel execution of Simulated Annealing and Variable Neighborhood Descent.
To compile the code for Parallel SA and VND, run the following command:
g++ -std=c++11 -fopenmp algorithm.cpp -o vrptw_solver
Replace algorithm.cpp
with the respective source file.
The input file follows the standard Solomon VRPTW benchmark format:
<Number of Customers> <Vehicle Capacity>
<Node ID> <X-Coordinate> <Y-Coordinate> <Demand> <Ready Time> <Due Time> <Service Time>
...
The output includes the generated routes and their associated metrics as follows:
<Number of Vehicles> <Total Distance> <Computation Time in ms>
<Number of Nodes in Route 1 (excluding depots)> <Route Nodes> <Arrival Times> <Vehicle Load>
<Number of Nodes in Route 2 (excluding depots)> <Route Nodes> <Arrival Times> <Vehicle Load>
...
The project is organized into the following folder structure:
project_root/
│
├── algorithms/
│ ├── 1-constructive.cpp
│ │── 2-constructive-with-noise.cpp
│ ├── 3-grasp.cpp
│ ├── 4-parallel-sa.cpp
│ ├── 5-parallel-vnd.cpp
│ └── 6-genetic-algorithm.cpp
│
├── results/
│ ├── 1-constructive.xlsx
│ ├── 2-constructive-with-noise.xlsx
│ └── ...
│
├── presentations/
│ ├── 1-constructivos.pdf
│ │── 2-metaheurísticos.pdf
│ └── 3-evolutivos.pdf
│
├── instances/
│ ├── VRPTW1.txt
│ ├── VRPTW2.txt
│ └── ...
│
└── README.md
algorithms/
: Contains the C++ source files implementing the heuristic and metaheuristic algorithms.results/
: Stores the respective output files for the test instances, including routes, number of vehicles, and total distances.presentations/
: Includes a detailed PDF presentation (in Spanish) explaining the implemented methods, their logic, and the experimental setup.README.md
: The current documentation file describing the project structure, setup, algorithms, and performance analysis.