|
| 1 | +//! Graph helpers. |
| 2 | +
|
| 3 | +use crate::bit::BitIterator; |
| 4 | +use crate::number::UnsignedInteger; |
| 5 | +use std::marker::PhantomData; |
| 6 | + |
| 7 | +/// Explore all hamiltonian paths/cycles in a graph. |
| 8 | +/// |
| 9 | +/// # Panics |
| 10 | +/// This function panics if the number of vertices exceeds the maximum supported, currently 32. |
| 11 | +/// |
| 12 | +/// # Examples |
| 13 | +/// |
| 14 | +/// Shortest hamiltonian path and cycle starting at vertex 0: |
| 15 | +/// ``` |
| 16 | +/// # use utils::graph::explore_hamiltonian_paths; |
| 17 | +/// # use std::ops::Add; |
| 18 | +/// let distance_matrix = vec![ |
| 19 | +/// 0, 7, 2, 8, 6, |
| 20 | +/// 7, 0, 1, 3, 5, |
| 21 | +/// 2, 1, 0, 9, 4, |
| 22 | +/// 8, 3, 9, 0, 9, |
| 23 | +/// 6, 5, 4, 9, 0, |
| 24 | +/// ]; |
| 25 | +/// |
| 26 | +/// let mut min_hamiltonian_path = u32::MAX; |
| 27 | +/// let mut min_hamiltonian_cycle = u32::MAX; |
| 28 | +/// explore_hamiltonian_paths( |
| 29 | +/// 5, // Number of vertices |
| 30 | +/// 0, // Start vertex |
| 31 | +/// 0, // Initial path cost |
| 32 | +/// |a, b| distance_matrix[a as usize * 5 + b as usize], // Distance function |
| 33 | +/// u32::add, // Accumulate function |
| 34 | +/// |path, loop_edge| { // Callback function |
| 35 | +/// min_hamiltonian_path = min_hamiltonian_path.min(path); |
| 36 | +/// min_hamiltonian_cycle = min_hamiltonian_cycle.min(path + loop_edge); |
| 37 | +/// } |
| 38 | +/// ); |
| 39 | +/// |
| 40 | +/// assert_eq!(min_hamiltonian_path, 14); // 0 =[6]=> 4 =[4]=> 2 =[1]=> 1 =[2]=> 3 |
| 41 | +/// assert_eq!(min_hamiltonian_cycle, 21); // 0 =[2]=> 2 =[1]=> 1 =[3]=> 3 =[9]=> 4 =[6]=> 0 |
| 42 | +/// ``` |
| 43 | +/// |
| 44 | +/// Shortest and longest hamilton paths starting at any vertex: |
| 45 | +/// ``` |
| 46 | +/// # use utils::graph::explore_hamiltonian_paths; |
| 47 | +/// let distance_matrix = vec![ |
| 48 | +/// 0, 7, 2, 8, 6, |
| 49 | +/// 7, 0, 1, 3, 5, |
| 50 | +/// 2, 1, 0, 9, 4, |
| 51 | +/// 8, 3, 9, 0, 9, |
| 52 | +/// 6, 5, 4, 9, 0, |
| 53 | +/// ]; |
| 54 | +/// |
| 55 | +/// let mut min_path = u32::MAX; |
| 56 | +/// let mut max_path = 0; |
| 57 | +/// explore_hamiltonian_paths( |
| 58 | +/// 5, // Number of vertices |
| 59 | +/// 0, // Start vertex |
| 60 | +/// (0, u32::MAX, 0), // Initial path cost |
| 61 | +/// |a, b| distance_matrix[a as usize * 5 + b as usize], // Distance function |
| 62 | +/// |(total, min_edge, max_edge), edge| { // Accumulate function |
| 63 | +/// (total + edge, min_edge.min(edge), max_edge.max(edge)) |
| 64 | +/// }, |
| 65 | +/// |(total, min_edge, max_edge), loop_edge| { // Callback function |
| 66 | +/// let loop_total = total + loop_edge; |
| 67 | +/// min_path = min_path.min(loop_total - max_edge.max(loop_edge)); |
| 68 | +/// max_path = max_path.max(loop_total - min_edge.min(loop_edge)); |
| 69 | +/// } |
| 70 | +/// ); |
| 71 | +/// |
| 72 | +/// assert_eq!(min_path, 12); // 0 =[6]=> 4 =[4]=> 2 =[1]=> 1 =[2]=> 3 |
| 73 | +/// assert_eq!(max_path, 31); // 1 =[7]=> 0 =[6]=> 4 =[9]=> 3 =[9]=> 2 |
| 74 | +/// ``` |
| 75 | +#[inline] |
| 76 | +pub fn explore_hamiltonian_paths<E, P: Copy>( |
| 77 | + vertices: u32, |
| 78 | + start_vertex: u32, |
| 79 | + initial_path: P, |
| 80 | + distance_fn: impl Fn(u32, u32) -> E, |
| 81 | + accumulate_fn: impl Fn(P, E) -> P, |
| 82 | + callback_fn: impl FnMut(P, E), |
| 83 | +) { |
| 84 | + // Rust doesn't allow recursive FnMut closures, so move state into struct |
| 85 | + struct Visitor<P, F, G, H> { |
| 86 | + start_vertex: u32, |
| 87 | + phantom: PhantomData<P>, |
| 88 | + distance_fn: F, |
| 89 | + accumulate_fn: G, |
| 90 | + callback_fn: H, |
| 91 | + } |
| 92 | + |
| 93 | + impl<E, P: Copy, F: Fn(u32, u32) -> E, G: Fn(P, E) -> P, H: FnMut(P, E)> Visitor<P, F, G, H> { |
| 94 | + #[inline] |
| 95 | + fn visit<V: UnsignedInteger>(&mut self, current_vertex: u32, visited: V, path: P) { |
| 96 | + if visited == V::MAX { |
| 97 | + let loop_edge = (self.distance_fn)(current_vertex, self.start_vertex); |
| 98 | + (self.callback_fn)(path, loop_edge); |
| 99 | + return; |
| 100 | + } |
| 101 | + |
| 102 | + for (next_vertex, next_bit) in BitIterator::zeroes(visited) { |
| 103 | + let new_edge = (self.distance_fn)(current_vertex, next_vertex); |
| 104 | + let new_path = (self.accumulate_fn)(path, new_edge); |
| 105 | + self.visit(next_vertex, visited | next_bit, new_path); |
| 106 | + } |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + // visit can take any unsigned integer type as a bitmask, so the code could switch the |
| 111 | + // implementation based on the number of vertices. However, it seems unlikely that an AoC |
| 112 | + // problem would require this as generating that many permutations would be very slow, so for |
| 113 | + // now assume u32 is fine. |
| 114 | + assert!(vertices <= 32, "too many vertices"); |
| 115 | + |
| 116 | + let mut visitor = Visitor { |
| 117 | + start_vertex, |
| 118 | + distance_fn, |
| 119 | + accumulate_fn, |
| 120 | + callback_fn, |
| 121 | + phantom: PhantomData, |
| 122 | + }; |
| 123 | + visitor.visit( |
| 124 | + start_vertex, |
| 125 | + !(u32::MAX >> (u32::BITS - vertices)) | (1 << start_vertex), |
| 126 | + initial_path, |
| 127 | + ); |
| 128 | +} |
0 commit comments