Skip to content

Commit 115abe2

Browse files
committed
2016 day 24
1 parent 8667709 commit 115abe2

File tree

10 files changed

+298
-106
lines changed

10 files changed

+298
-106
lines changed

crates/utils/src/graph.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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+
}

crates/utils/src/grid.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn from_str<T>(
7575
/// ```
7676
/// # use utils::grid::from_str_padded;
7777
/// assert_eq!(
78-
/// from_str_padded("##.#\n#..#\n#.##", 2, |c| match c {
78+
/// from_str_padded("##.#\n#..#\n#.##", 2, false, |c| match c {
7979
/// b'#' => Some(true),
8080
/// b'.' => Some(false),
8181
/// _ => None,
@@ -91,9 +91,10 @@ pub fn from_str<T>(
9191
/// ]),
9292
/// );
9393
/// ```
94-
pub fn from_str_padded<T: Clone + Default>(
94+
pub fn from_str_padded<T: Clone>(
9595
input: &str,
9696
padding: usize,
97+
padding_value: T,
9798
mut func: impl FnMut(u8) -> Option<T>,
9899
) -> Result<(usize, usize, Vec<T>), InputError> {
99100
let mut data = Vec::with_capacity(input.len());
@@ -107,7 +108,7 @@ pub fn from_str_padded<T: Clone + Default>(
107108
let padded_columns = columns + 2 * padding;
108109

109110
// Add initial padding rows + padding for start of first actual row
110-
data.resize(padded_columns * padding + padding, Default::default());
111+
data.resize(padded_columns * padding + padding, padding_value.clone());
111112

112113
for line in lines {
113114
if line.len() != columns {
@@ -127,13 +128,13 @@ pub fn from_str_padded<T: Clone + Default>(
127128
}
128129

129130
// Add padding for the end of the current row, and the start of the next row
130-
data.resize(data.len() + 2 * padding, Default::default());
131+
data.resize(data.len() + 2 * padding, padding_value.clone());
131132
}
132133

133134
// Add final padding rows, minus the already added padding for the start of a row
134135
data.resize(
135136
data.len() + padded_columns * padding - padding,
136-
Default::default(),
137+
padding_value,
137138
);
138139

139140
let rows = data.len() / padded_columns;

crates/utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
pub mod bit;
55
pub mod date;
66
mod framework;
7+
pub mod graph;
78
pub mod grid;
89
pub mod input;
910
pub mod md5;

crates/utils/src/number.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ pub trait Number:
3333
{
3434
const ZERO: Self;
3535
const ONE: Self;
36+
const MIN: Self;
37+
const MAX: Self;
3638

3739
#[must_use]
3840
fn abs(self) -> Self;
39-
4041
#[must_use]
4142
fn rem_euclid(self, rhs: Self) -> Self;
4243
}
@@ -66,9 +67,6 @@ pub trait Integer:
6667
+ ShrAssign<u32>
6768
+ TryInto<i128>
6869
{
69-
const MIN: Self;
70-
const MAX: Self;
71-
7270
#[must_use]
7371
fn checked_add(self, rhs: Self) -> Option<Self>;
7472
#[must_use]
@@ -92,6 +90,8 @@ macro_rules! number_impl {
9290
$(impl Number for $t {
9391
const ZERO: Self = 0;
9492
const ONE: Self = 1;
93+
const MIN: Self = Self::MIN;
94+
const MAX: Self = Self::MAX;
9595

9696
#[inline]
9797
fn abs(self) -> Self {
@@ -112,6 +112,8 @@ macro_rules! number_impl {
112112
$(impl Number for $t {
113113
const ZERO: Self = 0;
114114
const ONE: Self = 1;
115+
const MIN: Self = Self::MIN;
116+
const MAX: Self = Self::MAX;
115117

116118
#[inline]
117119
fn abs(self) -> Self {
@@ -133,6 +135,8 @@ macro_rules! number_impl {
133135
$(impl Number for $t {
134136
const ZERO: Self = 0.0;
135137
const ONE: Self = 1.0;
138+
const MIN: Self = Self::NEG_INFINITY;
139+
const MAX: Self = Self::INFINITY;
136140

137141
#[inline]
138142
fn abs(self) -> Self {
@@ -154,9 +158,6 @@ macro_rules! number_impl {
154158
};
155159
(@common integer => $($t:ty),+) => {
156160
$(impl Integer for $t {
157-
const MIN: Self = Self::MIN;
158-
const MAX: Self = Self::MAX;
159-
160161
#[inline]
161162
fn checked_add(self, rhs: Self) -> Option<Self> {
162163
self.checked_add(rhs)

crates/year2015/src/day09.rs

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::collections::HashMap;
2-
use utils::bit::BitIterator;
2+
use utils::graph::explore_hamiltonian_paths;
33
use utils::prelude::*;
44

55
/// Finding the shortest and longest path.
@@ -31,7 +31,8 @@ impl Day09 {
3131
indexes.entry(end).or_insert(len);
3232
});
3333

34-
if indexes.len() > Visited::BITS as usize {
34+
let locations = indexes.len();
35+
if locations > Visited::BITS as usize {
3536
return Err(InputError::new(input, 0, "too many locations"));
3637
}
3738

@@ -43,7 +44,21 @@ impl Day09 {
4344
matrix[indexes.len() * end + start] = dist;
4445
});
4546

46-
let (part1, part2) = Visitor::visit_all(matrix, indexes.len() as u32);
47+
let (mut part1, mut part2) = (u32::MAX, 0);
48+
explore_hamiltonian_paths(
49+
indexes.len() as u32,
50+
0,
51+
(0, u32::MAX, 0),
52+
|a, b| matrix[a as usize * locations + b as usize],
53+
|(total, min_edge, max_edge), edge| {
54+
(total + edge, min_edge.min(edge), max_edge.max(edge))
55+
},
56+
|(total, min_edge, max_edge), loop_edge| {
57+
part1 = part1.min(total + loop_edge - max_edge.max(loop_edge));
58+
part2 = part2.max(total + loop_edge - min_edge.min(loop_edge))
59+
},
60+
);
61+
4762
Ok(Self { part1, part2 })
4863
}
4964

@@ -58,53 +73,6 @@ impl Day09 {
5873
}
5974
}
6075

61-
struct Visitor {
62-
matrix: Vec<u32>,
63-
locations: u32,
64-
min: u32,
65-
max: u32,
66-
}
67-
68-
impl Visitor {
69-
#[must_use]
70-
fn visit_all(matrix: Vec<u32>, locations: u32) -> (u32, u32) {
71-
let mut v = Self {
72-
matrix,
73-
locations,
74-
min: u32::MAX,
75-
max: 0,
76-
};
77-
v.visit(
78-
0,
79-
!(Visited::MAX >> (Visited::BITS - locations)) | 1,
80-
0,
81-
u32::MAX,
82-
0,
83-
);
84-
(v.min, v.max)
85-
}
86-
87-
fn visit(&mut self, prev: u32, visited: Visited, distance: u32, min_edge: u32, max_edge: u32) {
88-
if visited == Visited::MAX {
89-
let loop_edge = self.matrix[prev as usize];
90-
self.min = self.min.min(distance + loop_edge - max_edge.max(loop_edge));
91-
self.max = self.max.max(distance + loop_edge - min_edge.min(loop_edge));
92-
return;
93-
}
94-
95-
for (next, next_bit) in BitIterator::zeroes(visited) {
96-
let edge = self.matrix[(prev * self.locations + next) as usize];
97-
self.visit(
98-
next,
99-
visited | next_bit,
100-
distance + edge,
101-
min_edge.min(edge),
102-
max_edge.max(edge),
103-
);
104-
}
105-
}
106-
}
107-
10876
examples!(Day09 -> (u32, u32) [
10977
{
11078
input: "London to Dublin = 464\n\

0 commit comments

Comments
 (0)