Skip to content

Commit 4ab542f

Browse files
committed
Optimize 2025 day 2 further
Calculate first and last terms directly, use sum formula for single sequences
1 parent 93a8c80 commit 4ab542f

File tree

1 file changed

+45
-76
lines changed

1 file changed

+45
-76
lines changed

crates/year2025/src/day02.rs

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -28,106 +28,75 @@ impl Day02 {
2828

2929
fn invalid_id_sum(&self, repeat_filter: impl Fn(u32) -> bool) -> u64 {
3030
let mut total = 0;
31-
let mut streams = Vec::new();
31+
let mut patterns = Vec::new();
3232

3333
for &[start, end] in &self.input {
34-
let min_digits = start.ilog10() + 1;
35-
let max_digits = end.ilog10() + 1;
36-
for pattern_digits in 1..=max_digits / 2 {
37-
for digits in (min_digits.next_multiple_of(pattern_digits)..=max_digits)
38-
.step_by(pattern_digits as usize)
34+
let min_digits = start.checked_ilog10().unwrap_or(0) + 1;
35+
let max_digits = end.checked_ilog10().unwrap_or(0) + 1;
36+
for repeat_digits in 1..=max_digits / 2 {
37+
for digits in (min_digits.next_multiple_of(repeat_digits)..=max_digits)
38+
.step_by(repeat_digits as usize)
3939
{
40-
let repeats = digits / pattern_digits;
40+
let repeats = digits / repeat_digits;
4141
if !repeat_filter(repeats) {
4242
continue;
4343
}
4444

45-
let pow10 = 10u64.pow(pattern_digits);
46-
let block = if digits == min_digits {
47-
start / 10u64.pow(min_digits - pattern_digits)
45+
let pow10 = 10u64.pow(repeat_digits);
46+
47+
// Mask that repeats the pattern (e.g. 1,001,001 for 3x 3 digits)
48+
let repeat_mask = (0..repeats).fold(0, |acc, _| acc * pow10 + 1);
49+
50+
// Smallest number matching the repeated pattern that is >= start
51+
let range_start = if digits == min_digits {
52+
// Repeat the highest N digits of start, + repeat_mask if smaller than the start
53+
let x = (start / 10u64.pow(min_digits - repeat_digits)) * repeat_mask;
54+
if x < start { x + repeat_mask } else { x }
4855
} else {
49-
pow10 / 10
56+
(pow10 / 10) * repeat_mask
5057
};
5158

52-
streams.push(
53-
RepeatingIdStream {
54-
block,
55-
pow10,
56-
repeats,
57-
range_start: start,
58-
range_end: end,
59-
}
60-
.peekable(),
61-
)
59+
// Largest number matching the repeated pattern that is <= end
60+
let range_end = if digits == max_digits {
61+
let x = (end / 10u64.pow(max_digits - repeat_digits)) * repeat_mask;
62+
x.min(end)
63+
} else {
64+
(pow10 - 1) * repeat_mask
65+
};
66+
67+
if range_start > range_end || range_end > end {
68+
continue;
69+
}
70+
71+
patterns.push((range_start, range_end, repeat_mask));
6272
}
6373
}
6474

65-
let mut previous_min = None;
66-
loop {
67-
let mut min = None;
75+
// Merge and deduplicate multiple sequences
76+
while patterns.len() > 1 {
77+
let min = patterns.iter().map(|&(n, _, _)| n).min().unwrap();
78+
total += min;
6879

69-
// Advance past previous_min (if any), find the next min, and remove any empy streams
70-
streams.retain_mut(|s| {
71-
if s.peek().copied() == previous_min {
72-
let _ = s.next();
73-
}
74-
75-
if let Some(&next) = s.peek() {
76-
if min.is_none_or(|n| n > next) {
77-
min = Some(next);
78-
}
79-
true
80-
} else {
81-
false
80+
patterns.retain_mut(|(n, end, offset)| {
81+
if *n == min {
82+
*n += *offset;
8283
}
84+
*n <= *end
8385
});
86+
}
8487

85-
let Some(min) = min else {
86-
break;
87-
};
88-
89-
total += min;
90-
previous_min = Some(min);
88+
// Use the formula for the sum of the arithmetic sequence to compute the final sequence
89+
if let Some((start, end, step)) = patterns.pop() {
90+
let n = ((end - start) / step) + 1;
91+
let last = start + (n - 1) * step;
92+
total += n * (start + last) / 2;
9193
}
9294
}
9395

9496
total
9597
}
9698
}
9799

98-
#[derive(Clone, Debug)]
99-
struct RepeatingIdStream {
100-
block: u64,
101-
pow10: u64,
102-
repeats: u32,
103-
range_start: u64,
104-
range_end: u64,
105-
}
106-
107-
impl Iterator for RepeatingIdStream {
108-
type Item = u64;
109-
110-
fn next(&mut self) -> Option<Self::Item> {
111-
while self.block < self.pow10 {
112-
let mut n = 0;
113-
for _ in 0..self.repeats {
114-
n = n * self.pow10 + self.block;
115-
}
116-
self.block += 1;
117-
118-
if n > self.range_end {
119-
// No more solutions in this stream
120-
self.block = self.pow10;
121-
return None;
122-
}
123-
if n >= self.range_start {
124-
return Some(n);
125-
}
126-
}
127-
None
128-
}
129-
}
130-
131100
examples!(Day02 -> (u64, u64) [
132101
{
133102
input: "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,\

0 commit comments

Comments
 (0)