From 9a9545425a14b374a5b97bce68469ecb3db26209 Mon Sep 17 00:00:00 2001 From: kageru Date: Tue, 5 Dec 2023 12:46:57 +0100 Subject: [PATCH] optimize 2023/05/2 --- 2023/src/bin/day05.rs | 44 +++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/2023/src/bin/day05.rs b/2023/src/bin/day05.rs index 161f01e..7f65717 100644 --- a/2023/src/bin/day05.rs +++ b/2023/src/bin/day05.rs @@ -41,26 +41,46 @@ fn resolve_backwards(start: I, mappings: &Vec) -> I { fn part2((seeds, mappings): &Parsed) -> I { let seed_ranges = seeds.iter().array_chunks().map(|[&a, &b]| a..a + b).collect_vec(); let mut mappings = mappings.clone(); - // let smallest_range = mappings.iter().flatten().map(|(r, _, _)| r.try_len().unwrap()).min().unwrap(); - - let mut destinations = mappings.remove(mappings.len() - 1); - destinations.sort_by_key(|(_, range, _)| range.start); mappings.reverse(); + let mut destinations = mappings.remove(0); + destinations.sort_by_key(|(_, range, _)| range.start); + let mut ranges = Vec::<(Range, I)>::new(); for (_, range, offset) in destinations { - ranges.push((ranges.last().map(|(r, _)| r.end).unwrap_or(1)..range.start, 0)); + ranges.push((ranges.last().map(|(r, _)| r.end).unwrap_or(0)..range.start, 0)); ranges.push((range, offset)); } - ranges.into_iter().find_map(|(range, offset)| starting_seed(range, offset, &seed_ranges, &mappings, 1)).unwrap() + + // By calculating the smallest range in the input data, we can perform something similar to binary search. + // We start at location 0 and move in steps of this size, + // and when we first encounter a location that corresponds to a starting seed we start binary searching, + // i.e. we half the step size and move back by that until we find the lowest location that has a starting seed. + let mut step = mappings.iter().flatten().map(|(r, _, _)| r.try_len().unwrap()).min().unwrap() as I; + ranges + .into_iter() + .find_map(|(range, offset)| { + let mut best = I::MAX; + let mut location = range.start + step; + while location < range.end && step != 0 { + if has_starting_seed(location, offset, &seed_ranges, &mappings) { + best = best.min(location); + step = (step >> 1) + 1; + location -= step; + } else { + step >>= (best != I::MAX) as usize; + location += step; + } + } + (best != I::MAX).then_some(best) + }) + .unwrap() } -fn starting_seed(range: Range, offset: i64, seed_ranges: &Vec>, mappings: &Vec, step: usize) -> Option { - range.step_by(step).find(|&s| { - let seed = resolve_backwards(s - offset, &mappings); - // If seed == s, the entire resolution didn’t hit a single mapping, so we don’t need to check seeds. - seed != s && seed_ranges.iter().any(|r| r.contains(&seed)) - }) +fn has_starting_seed(start: I, offset: i64, seed_ranges: &Vec>, mappings: &Vec) -> bool { + let seed = resolve_backwards(start - offset, &mappings); + // If seed == s, the entire resolution didn’t hit a single mapping, so we don’t need to check seeds. + seed != start && seed_ranges.iter().any(|r| r.contains(&seed)) } boilerplate! {