mirror of
https://github.com/RussellChamp/advent-of-code-2025-cursor.git
synced 2026-10-27 23:52:34 +00:00
🚀 Day 2: MASSIVE 10x speedup & 99% memory reduction! Iterator magic FTW!
This commit is contained in:
1 parent
f95ecc2fe1
commit
5e0660b095
2 files changed
+69
-54
No files matched your search
@@ -12,12 +12,12 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
|
|||||||
| Day | Part 1 | Part 2 |
|
| Day | Part 1 | Part 2 |
|
||||||
| :---: | :---: | :---: |
|
| :---: | :---: | :---: |
|
||||||
| [Day 1](./src/bin/01.rs) | `102.5µs` | `123.5µs` |
|
| [Day 1](./src/bin/01.rs) | `102.5µs` | `123.5µs` |
|
||||||
| [Day 2](./src/bin/02.rs) | `1.1ms` | `3.8ms` |
|
| [Day 2](./src/bin/02.rs) | `131.1µs` | `314.3µs` |
|
||||||
| [Day 3](./src/bin/03.rs) | `85.1µs` | `121.6µs` |
|
| [Day 3](./src/bin/03.rs) | `85.1µs` | `121.6µs` |
|
||||||
| [Day 4](./src/bin/04.rs) | `244.2µs` | `4.1ms` |
|
| [Day 4](./src/bin/04.rs) | `244.2µs` | `4.1ms` |
|
||||||
| [Day 5](./src/bin/05.rs) | `196.7µs` | `66.3µs` |
|
| [Day 5](./src/bin/05.rs) | `196.7µs` | `66.3µs` |
|
||||||
|
|
||||||
**Total: 9.94ms**
|
**Total: 5.49ms**
|
||||||
<!--- benchmarking table --->
|
<!--- benchmarking table --->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+67
-52
@@ -12,38 +12,11 @@ fn parse_range(s: &str) -> Option<(u64, u64)> {
|
|||||||
.and_then(|(a, b)| Some((a?, b?)))
|
.and_then(|(a, b)| Some((a?, b?)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate all invalid numbers (pattern repeated exactly twice) within [min, max]
|
/// Iterator over invalid numbers (pattern repeated exactly twice) within [min, max]
|
||||||
fn generate_invalid_part1(min: u64, max: u64) -> Vec<u64> {
|
fn iter_invalid_part1(min: u64, max: u64) -> impl Iterator<Item = u64> {
|
||||||
let mut result = Vec::new();
|
let max_digits = if max == 0 { 1 } else { max.ilog10() as usize + 1 };
|
||||||
let max_digits = max.ilog10() as usize + 1;
|
|
||||||
|
|
||||||
// Pattern length k produces numbers with 2k digits
|
(1..=max_digits / 2).flat_map(move |pattern_len| {
|
||||||
for pattern_len in 1..=max_digits / 2 {
|
|
||||||
let pattern_min = if pattern_len == 1 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
10u64.pow(pattern_len as u32 - 1)
|
|
||||||
};
|
|
||||||
let pattern_max = 10u64.pow(pattern_len as u32) - 1;
|
|
||||||
|
|
||||||
for pattern in pattern_min..=pattern_max {
|
|
||||||
// Construct number by repeating pattern twice
|
|
||||||
let num = pattern * 10u64.pow(pattern_len as u32) + pattern;
|
|
||||||
if num >= min && num <= max {
|
|
||||||
result.push(num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate all invalid numbers (pattern repeated at least twice) within [min, max]
|
|
||||||
fn generate_invalid_part2(min: u64, max: u64) -> Vec<u64> {
|
|
||||||
let mut seen = HashSet::new();
|
|
||||||
let max_digits = max.ilog10() as usize + 1;
|
|
||||||
|
|
||||||
// For each pattern length
|
|
||||||
for pattern_len in 1..=max_digits / 2 {
|
|
||||||
let pattern_min = if pattern_len == 1 {
|
let pattern_min = if pattern_len == 1 {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
@@ -52,36 +25,78 @@ fn generate_invalid_part2(min: u64, max: u64) -> Vec<u64> {
|
|||||||
let pattern_max = 10u64.pow(pattern_len as u32) - 1;
|
let pattern_max = 10u64.pow(pattern_len as u32) - 1;
|
||||||
let multiplier = 10u64.pow(pattern_len as u32);
|
let multiplier = 10u64.pow(pattern_len as u32);
|
||||||
|
|
||||||
// For each number of repetitions (at least 2)
|
(pattern_min..=pattern_max).filter_map(move |pattern| {
|
||||||
for reps in 2..=max_digits / pattern_len {
|
let num = pattern * multiplier + pattern;
|
||||||
for pattern in pattern_min..=pattern_max {
|
if num >= min && num <= max {
|
||||||
// Build number by repeating pattern
|
Some(num)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over invalid numbers (pattern repeated at least twice) within [min, max]
|
||||||
|
fn iter_invalid_part2(min: u64, max: u64) -> impl Iterator<Item = u64> {
|
||||||
|
let max_digits = if max == 0 { 1 } else { max.ilog10() as usize + 1 };
|
||||||
|
|
||||||
|
(1..=max_digits / 2).flat_map(move |pattern_len| {
|
||||||
|
let pattern_min = if pattern_len == 1 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
10u64.pow(pattern_len as u32 - 1)
|
||||||
|
};
|
||||||
|
let pattern_max = 10u64.pow(pattern_len as u32) - 1;
|
||||||
|
let multiplier = 10u64.pow(pattern_len as u32);
|
||||||
|
|
||||||
|
(2..=max_digits / pattern_len).flat_map(move |reps| {
|
||||||
|
(pattern_min..=pattern_max).filter_map(move |pattern| {
|
||||||
let mut num = 0u64;
|
let mut num = 0u64;
|
||||||
for _ in 0..reps {
|
for _ in 0..reps {
|
||||||
num = num * multiplier + pattern;
|
num = num * multiplier + pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if num >= min && num <= max {
|
if num >= min && num <= max {
|
||||||
seen.insert(num);
|
Some(num)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Merge overlapping ranges to avoid duplicate counting
|
||||||
|
fn merge_ranges(ranges: &[(u64, u64)]) -> Vec<(u64, u64)> {
|
||||||
|
if ranges.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut sorted: Vec<_> = ranges.to_vec();
|
||||||
|
sorted.sort_by_key(|r| r.0);
|
||||||
|
|
||||||
|
let mut merged = Vec::with_capacity(sorted.len());
|
||||||
|
merged.push(sorted[0]);
|
||||||
|
|
||||||
|
for &(start, end) in &sorted[1..] {
|
||||||
|
let last = merged.last_mut().unwrap();
|
||||||
|
if start <= last.1 + 1 {
|
||||||
|
last.1 = last.1.max(end);
|
||||||
|
} else {
|
||||||
|
merged.push((start, end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seen.into_iter().collect()
|
merged
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part_one(input: &str) -> Option<u64> {
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
let ranges: Vec<(u64, u64)> = input.split(',').filter_map(parse_range).collect();
|
let ranges: Vec<(u64, u64)> = input.split(',').filter_map(parse_range).collect();
|
||||||
|
let merged = merge_ranges(&ranges);
|
||||||
|
|
||||||
let global_min = ranges.iter().map(|r| r.0).min().unwrap_or(0);
|
// Sum invalid numbers directly from iterator, checking merged ranges
|
||||||
let global_max = ranges.iter().map(|r| r.1).max().unwrap_or(0);
|
let sum: u64 = merged
|
||||||
|
|
||||||
let invalid_nums = generate_invalid_part1(global_min, global_max);
|
|
||||||
|
|
||||||
let sum = invalid_nums
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&&n| ranges.iter().any(|&(start, end)| n >= start && n <= end))
|
.flat_map(|&(start, end)| iter_invalid_part1(start, end))
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
Some(sum)
|
Some(sum)
|
||||||
@@ -89,15 +104,15 @@ pub fn part_one(input: &str) -> Option<u64> {
|
|||||||
|
|
||||||
pub fn part_two(input: &str) -> Option<u64> {
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
let ranges: Vec<(u64, u64)> = input.split(',').filter_map(parse_range).collect();
|
let ranges: Vec<(u64, u64)> = input.split(',').filter_map(parse_range).collect();
|
||||||
|
let merged = merge_ranges(&ranges);
|
||||||
|
|
||||||
let global_min = ranges.iter().map(|r| r.0).min().unwrap_or(0);
|
// Use a small HashSet only for deduplication within merged ranges
|
||||||
let global_max = ranges.iter().map(|r| r.1).max().unwrap_or(0);
|
// (needed because same number can match multiple pattern lengths)
|
||||||
|
let mut seen = HashSet::new();
|
||||||
let invalid_nums = generate_invalid_part2(global_min, global_max);
|
let sum: u64 = merged
|
||||||
|
|
||||||
let sum = invalid_nums
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&&n| ranges.iter().any(|&(start, end)| n >= start && n <= end))
|
.flat_map(|&(start, end)| iter_invalid_part2(start, end))
|
||||||
|
.filter(|&n| seen.insert(n))
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
Some(sum)
|
Some(sum)
|
||||||
|
|||||||
Reference in new issue
Block a user