A C++ implementation of a Large Neighborhood Search (LNS) metaheuristic for solving feeder bus routing optimization problems with flexible demand-responsive services. The code is used in this academic paper.
This project implements an optimization algorithm for bus routing that:
- Assigns passengers to buses while respecting capacity constraints
- Determines optimal station sequences for each bus route
- Minimizes total cost considering bus travel time, passenger walking time, and arrival time deviations
- Uses a destroy-and-repair heuristic combined with 2-opt local search and Simulated Annealing
The algorithm solves a feeder bus routing problem with:
- Mandatory stations: Fixed stops that must be visited in sequence
- Optional stations: Additional stops that can be selectively included
- Time windows: Constraints on passenger desired arrival times
- Capacity constraints: Maximum passengers per bus
- Walking distance limits: Maximum distance passengers can walk to stations
The cost function minimizes:
Cost = c1 × (bus travel time + dwell time)
+ c2 × (passenger walking time)
+ c3 × (deviation from desired arrival time)
DRFS-LNS/
├── CMakeLists.txt # CMake build configuration
├── LICENSE # MIT License
├── README.md # This file
├── .vscode/ # VS Code configuration
│ ├── launch.json # Debugger settings
│ └── tasks.json # Build tasks
├── build/ # Build output directory (generated)
├── Data/ # Input/output data files
│ ├── instances/ # Problem instances
│ └── results/ # Solution outputs
├── include/ # Header files
│ ├── parameters/ # Parameter declarations
│ │ ├── hardcoded.h # Problem constants
│ │ ├── optimization.h # Decision variables
│ │ ├── others.h # Auxiliary variables
│ │ └── random_generators.h # Random number generators
│ └── sideFunctions/ # Utility function headers
│ ├── reporting.h # Output and reporting
│ └── sorting.h # Sorting algorithms
└── src/ # Source files
├── main.cpp # Main algorithm implementation
├── parameters/ # Parameter implementations
│ ├── hardcoded.cpp # Constant definitions
│ ├── optimization.cpp # Variable definitions
│ ├── others.cpp # Auxiliary variables and processing
│ └── random_generators.cpp # RNG implementations
└── sideFunctions/ # Utility implementations
├── reporting.cpp # Solution output functions
└── sorting.cpp # QuickSort and other sorting
- Problem constants: Number of buses, stations, passengers
- Physical constraints: Bus capacity, speeds, time limits
- Algorithm parameters: Iteration limits, destroy operations
- Cost weights: c1, c2, c3 for objective function
- Decision variables:
xk[i][p][j]: Passengerpassigned to stationjon busiyk[i][j][k]: Busitravels from stationjto stationkAk[i]: Arrival time for busiDk[i]: Idle/delay time for busi
- Solution tracking: Current, proposed, and best solutions
- Memory management: Functions for dynamic array allocation/deallocation
- Passenger data: Locations, desired arrival times
- Station data: Mandatory and optional station coordinates
- Travel time matrices: Pre-computed distances between all points
- Processing functions: Data loading and initialization
- RNG engine: Mersenne Twister random number generator
- Distributions:
rand_passenger: Random passenger selectionrand_station: Random station selectionrand_percentage: Percentage-based decisionsrand_bus_weighted: Weighted bus selectionrand_destroy_count: Number of passengers to destroy (3-5)
- QuickSort: Sorts buses by ranking values
- Median finding: Calculates median passenger arrival time
- Index search: Finds station positions in routes
- Solution output: Writes final solutions to files
- Progress tracking: Records cost improvements over time
- Instance saving: Stores problem parameters
The main algorithm follows this structure:
- Load parameters and process input data
- Sort passengers by desired arrival time
- Assign passengers to buses sequentially
- Construct initial routes using nearest neighbor heuristic
- Apply 2-opt local search for route improvement
while (iterations - last_improvement <= max_iterations):
1. DESTROY: Remove 3-5 randomly selected passengers
2. REASSIGN: Assign destroyed passengers to new buses
- Rank buses by arrival time similarity
- Use weighted selection favoring better-ranked buses
3. REBUILD: Reconstruct routes for affected buses
- Select closest eligible stations
- Build routes using greedy nearest neighbor
4. EVALUATE: Calculate cost of new solution
5. ACCEPT: If cost improved, update current solution
- Apply intensive 2-opt with Simulated Annealing
- Use nearest neighbor candidate lists
- Cool down temperature gradually
- Track best solution across all runs
- Randomly removes passengers from current solution
- Reassigns to different buses based on arrival time compatibility
- Allows exploration of different bus-passenger assignments
- Reverses route segments to reduce travel distance
- Applied after initialization and at the end
- Uses Simulated Annealing in final phase for better exploration
- Second/Third station selection: Probabilistically chooses non-optimal stations
- Weighted bus selection: Biases toward better-ranked buses but allows variety
- Random destroy count: Varies neighborhood size (3-5 passengers)
- Capacity: Ensures buses don't exceed maximum capacity
- Time windows: Respects passenger arrival time constraints
- Walking distance: Limits passenger-to-station walking time
- Route connectivity: Maintains valid station sequences
- CMake 3.22 or higher
- C++17 compatible compiler (Clang, GCC, or MSVC)
- (Optional) AddressSanitizer for memory debugging
# Create build directory
mkdir build
cd build
# Configure with CMake
cmake ..
# Build the project
cmake --build . --config Debug
# Run the executable
./DRFS_LNSThe project is configured to use AddressSanitizer in Debug mode for detecting memory issues:
cmake --build . --config DebugFor optimal performance:
cmake --build . --config ReleasePlace instance files in Data/instances/ directory with passenger locations and desired arrival times.
Edit values in src/parameters/hardcoded.cpp:
nRUN: Number of independent runsf_IT: Maximum iterations without improvementndestroy: Number of passengers to destroy per iterationc1, c2, c3: Cost function weightssecondS, thirdS: Station selection diversification thresholds
Results are written to Data/results/:
Runs_[instance].txt: Cost and runtime for each run- Solution files with detailed assignments and routes
- Progress tracking with improvement timestamps
The algorithm tracks:
- Best cost: Lowest objective value found
- Runtime: Computational time to best solution
- Last improvement time: When the best solution was discovered
- Progress curve: Cost evolution over iterations
The project includes VS Code configuration:
- Debug: F5 to build and debug with breakpoints
- Build tasks: Ctrl+Shift+B for quick compilation
- IntelliSense: Full code completion with compile_commands.json