From 7be15ccb91d995d58f93faa0bd68b343b92f40a3 Mon Sep 17 00:00:00 2001 From: 0x40ba72637962bbeb3da5c36780ff8adea7e9cdfb Date: Tue, 23 Apr 2024 15:34:38 +0000 Subject: [PATCH] "Player 0x40ba72637962bbeb3da5c36780ff8adea7e9cdfb submitted 'clarke_wright' for challenge vehicle_routing" --- .../src/vehicle_routing/clarke_wright.rs | 87 +++++++++++++++++++ tig-algorithms/src/vehicle_routing/mod.rs | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 tig-algorithms/src/vehicle_routing/clarke_wright.rs diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright.rs b/tig-algorithms/src/vehicle_routing/clarke_wright.rs new file mode 100644 index 00000000..ea192572 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright.rs @@ -0,0 +1,87 @@ +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::new(); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + Ok(Some(Solution { + routes: routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(), + })) +} diff --git a/tig-algorithms/src/vehicle_routing/mod.rs b/tig-algorithms/src/vehicle_routing/mod.rs index 62bb1af6..08cc4a91 100644 --- a/tig-algorithms/src/vehicle_routing/mod.rs +++ b/tig-algorithms/src/vehicle_routing/mod.rs @@ -1,4 +1,4 @@ -// c002_a001 placeholder +pub mod clarke_wright; // c002_a002 placeholder // c002_a003 placeholder // c002_a004 placeholder