Skip to content

Commit

Permalink
refactor(2023): simplify and increase performance of day 15 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 15, 2023
1 parent e007548 commit e0d915b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 52 deletions.
17 changes: 9 additions & 8 deletions rust/2023/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,20 @@ With the help of [cargo-aoc](https://github.com/gobanos/cargo-aoc) I get automat
| 12 | 2.65 ms | 149.26 ms | | 113.10 µs |
| 13 | 24.87 µs | 17.61 µs | | 169.28 µs |
| 14 | 19.30 µs | 20.22 µs | | 23.69 µs |
| 15 | 19.99 µs | 292.04 µs | | 241.12 µs |
| 15 | 19.99 µs | 210.88 µs | - / `-28.20%` | 241.12 µs |

\* compared to first solution<br/>
\*\* slow, didn't benchmark. Value comes from running the solver.

### Previous solutions

| Day | #1 | #2 | Improvement | Link |
| --: | ---------: | -------: | ----------: | ------------------------------------------------------------------------------------------------------------------------ |
| 3 | 172.23 µs | - | Baseline | [Link](https://github.com/believer/advent-of-code/blob/75a83e31024bbac99a0664f81fce4e13ec1e94af/rust/2023/src/day_03.rs) |
| 4 | 24.33 µs | 24.32 µs | Baseline | [Link](https://github.com/believer/advent-of-code/blob/c970c6322d3904048bcf3f30b1052e2916476d73/rust/2023/src/day_04.rs) |
| 5 | 1.50 µs | 21.22 s | Baseline | [Link](https://github.com/believer/advent-of-code/blob/39b0904c4921f4ae79963a6df49bb3502ef6b3be/rust/2023/src/day_05.rs) |
| 8 | - | 15.25 ms | Baseline | [Link](https://github.com/believer/advent-of-code/blob/912d70c6e04ffd97f766c79b90764c105fe2f6ce/rust/2023/src/day_08.rs) |
| 10 | 1.70 ms \* | - | Baseline | [Link](https://github.com/believer/advent-of-code/blob/ebbbbb8cb26e0fa4858cc48cf1a00304b4eee3a7/rust/2023/src/day_10.rs) |
| Day | #1 | #2 | Improvement | Link |
| --: | ---------: | --------: | ----------: | ------------------------------------------------------------------------------------------------------------------------ |
| 3 | 172.23 µs | - | Baseline | [Link](https://github.com/believer/advent-of-code/blob/75a83e31024bbac99a0664f81fce4e13ec1e94af/rust/2023/src/day_03.rs) |
| 4 | 24.33 µs | 24.32 µs | Baseline | [Link](https://github.com/believer/advent-of-code/blob/c970c6322d3904048bcf3f30b1052e2916476d73/rust/2023/src/day_04.rs) |
| 5 | 1.50 µs | 21.22 s | Baseline | [Link](https://github.com/believer/advent-of-code/blob/39b0904c4921f4ae79963a6df49bb3502ef6b3be/rust/2023/src/day_05.rs) |
| 8 | - | 15.25 ms | Baseline | [Link](https://github.com/believer/advent-of-code/blob/912d70c6e04ffd97f766c79b90764c105fe2f6ce/rust/2023/src/day_08.rs) |
| 10 | 1.70 ms \* | - | Baseline | [Link](https://github.com/believer/advent-of-code/blob/ebbbbb8cb26e0fa4858cc48cf1a00304b4eee3a7/rust/2023/src/day_10.rs) |
| 15 | - | 292.04 µs | Baseline | [Link](https://github.com/believer/advent-of-code/blob/5040f0fb6d9888b4a4d28f976e44ed077e0337b2/rust/2023/src/day_15.rs) |

\* BFS solution. I liked it, so it's still in the file.
84 changes: 40 additions & 44 deletions rust/2023/src/day_15.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
//! The first part was a quick one using the as_bytes() method on strings.
//! The second part was a bit trickier, but not too bad. The steps were
//! simple, but mutating the HashMap and the inner vec was the tricky part.
//!
//! Refactored part 2 to use one list for the boxes and a HashMap for the
//! current focal lengths. This made it easier, and faster, to mutate the boxes and
//! calculate the focusing power.

use std::collections::HashMap;

pub struct Input {
steps: Vec<String>,
}

enum Operation {
Add,
Remove,
}

#[aoc_generator(day15)]
pub fn input_generator(input: &str) -> Input {
Input {
Expand All @@ -25,11 +24,11 @@ pub fn input_generator(input: &str) -> Input {
}
}

fn hash(steps: &str) -> u32 {
fn hash(steps: &str) -> usize {
let mut current_value = 0;

for c in steps.as_bytes().iter() {
current_value += *c as u32;
current_value += *c as usize;
current_value *= 17;
current_value %= 256;
}
Expand All @@ -50,7 +49,7 @@ let data = include_str!("../input/2023/day15.txt");
assert_eq!(solve_part_01(&input_generator(data)), 516070);
```"#]
#[aoc(day15, part1)]
pub fn solve_part_01(input: &Input) -> u32 {
pub fn solve_part_01(input: &Input) -> usize {
input.steps.iter().map(|step| hash(step)).sum()
}

Expand All @@ -69,45 +68,42 @@ let data = include_str!("../input/2023/day15.txt");
assert_eq!(solve_part_02(&input_generator(data)), 244981);
```"#]
#[aoc(day15, part2)]
pub fn solve_part_02(input: &Input) -> u32 {
let mut boxes: HashMap<u32, Vec<String>> = HashMap::with_capacity(256);
pub fn solve_part_02(input: &Input) -> usize {
let mut boxes = vec![Vec::new(); 256];
let mut focal_lengths: HashMap<&str, usize> = HashMap::new();

for step in input.steps.iter() {
let operation = if step.contains('=') {
Operation::Add
} else {
Operation::Remove
};
let (label, _) = step.split_once(|c| c == '=' || c == '-').unwrap();
let key = hash(label);
let current_box = boxes.entry(key).or_default();

match operation {
Operation::Add => {
// Replace the lens if it already exists, otherwise add it
if let Some(index) = current_box.iter().position(|x| x.contains(label)) {
current_box[index] = step.to_string();
} else {
current_box.push(step.to_string());
}
}
Operation::Remove => {
if let Some(index) = current_box.iter().position(|x| x.contains(label)) {
current_box.remove(index);
}
// Add operation
if step.contains('=') {
let (label, focal_length) = step.split_once('=').unwrap();
let focal_length = focal_length.parse::<usize>().unwrap();
let key = hash(label);

focal_lengths.insert(label, focal_length);

if !boxes[key].contains(&label) {
boxes[key].push(label);
}
// Remove operation
} else {
let label = step[..step.len() - 1].to_string();
let key = hash(&label);

boxes[key].retain(|x| *x != label);
}
}

let mut sum = 0;

for (box_number, labels) in boxes.iter().enumerate() {
for (lens_slot, label) in labels.iter().enumerate() {
let focal_length = focal_lengths.get(label).unwrap();

sum += (box_number + 1) * (lens_slot + 1) * focal_length;
}
}

boxes
.iter()
.flat_map(|(box_number, sequences)| {
sequences.iter().enumerate().map(move |(index, sequence)| {
let (_, focal_length) = sequence.split_once('=').unwrap();
(box_number + 1) * (index as u32 + 1) * focal_length.parse::<u32>().unwrap()
})
})
.sum()
sum
}

#[cfg(test)]
Expand All @@ -118,19 +114,19 @@ mod tests {
#[rstest]
#[case("HASH", 52)]
#[case("rn=1", 30)]
fn hasher(#[case] input: &str, #[case] expected: u32) {
fn hasher(#[case] input: &str, #[case] expected: usize) {
assert_eq!(hash(input), expected);
}

#[rstest]
#[case("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", 1320)]
fn sample_01(#[case] input: &str, #[case] expected: u32) {
fn sample_01(#[case] input: &str, #[case] expected: usize) {
assert_eq!(solve_part_01(&input_generator(input)), expected);
}

#[rstest]
#[case("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", 145)]
fn sample_02(#[case] input: &str, #[case] expected: u32) {
fn sample_02(#[case] input: &str, #[case] expected: usize) {
assert_eq!(solve_part_02(&input_generator(input)), expected);
}
}

0 comments on commit e0d915b

Please sign in to comment.