Skip to content

Commit

Permalink
improve day 4
Browse files Browse the repository at this point in the history
  • Loading branch information
wowkster committed Dec 4, 2023
1 parent cc5a088 commit e8b4b67
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 54 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ default-run = "advent_of_code_2023"

[dependencies]
aoc = "0.4.0"
lazy_static = "1.4.0"
nom = "7.1.3"
rayon = "1.8.0"

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The following benchmarks were created on a Macbook Pro with an M2 Pro processor:
| [Day 1](https://github.com/wowkster/advent-of-code-2023/blob/main/src/bin/01.rs) | `24.2µs` | `120.8µs` |
| [Day 2](https://github.com/wowkster/advent-of-code-2023/blob/main/src/bin/02.rs) | `40.9µs` | `40.8µs` |
| [Day 3](https://github.com/wowkster/advent-of-code-2023/blob/main/src/bin/03.rs) | `260.7µs` | `116.3µs` |
| [Day 4](https://github.com/wowkster/advent-of-code-2023/blob/main/src/bin/04.rs) | `86.4µs` | `368.9µs` |
| [Day 4](https://github.com/wowkster/advent-of-code-2023/blob/main/src/bin/04.rs) | `85.2µs` | `104.7µs` |

## Project Structure

Expand Down
70 changes: 19 additions & 51 deletions src/bin/04.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

advent_of_code_2023::solution!(4);

use std::{collections::HashMap, sync::Mutex};
use std::collections::HashMap;

use nom::{
bytes::complete::{tag, take_while_m_n},
Expand All @@ -17,25 +17,30 @@ use rayon::prelude::*;
pub fn part_1(input: &str) -> Option<u32> {
let cards = parse_cards(input);

let sum = cards.iter().map(Card::score).sum();

Some(sum)
Some(cards.iter().map(Card::score).sum())
}

#[inline]
pub fn part_2(input: &str) -> Option<u32> {
// UNOPTIMIZATION: For the benchmark to be fair, we must ensure that the cache
// is cleared between iterations
COPIES_CACHE.lock().unwrap().clear();

let original_cards = parse_cards(input);

let total_cards = original_cards
.par_iter()
.map(|c| c.count_copies(&original_cards))
.sum::<u32>();
let mut solution: HashMap<u32, u32> = HashMap::new();

original_cards.iter().for_each(|card| {
// Include the original counts for each card id
*solution.entry(card.id).or_insert(0) += 1;

// Get the current count for the card
let current_amount = solution[&card.id];

// For each match, add our current amount to that card. This works becauase
// adding the current amount is like iterating it but cheaper.
for i in 0..card.matches {
*solution.entry(card.id + 1 + i).or_insert(0) += current_amount;
}
});

Some(total_cards)
Some(solution.values().sum())
}

#[derive(Debug, Clone)]
Expand All @@ -44,10 +49,6 @@ struct Card {
matches: u32,
}

lazy_static::lazy_static! {
static ref COPIES_CACHE: Mutex<HashMap<u32, u32>> = Mutex::default();
}

impl Card {
pub fn new(id: u32, winning_numbers: Vec<u32>, my_numbers: Vec<u32>) -> Self {
// OPTIMIZATION: Cache matches in constructor
Expand All @@ -66,37 +67,6 @@ impl Card {

2u32.pow(self.matches - 1)
}

pub fn count_copies(&self, original_cards: &[Card]) -> u32 {
// OPTIMIZATION: Caches the copies internally because the same card
// will always return the same number of new created cards

let cached_count = COPIES_CACHE.lock().unwrap().get(&self.id).copied();

if let Some(res) = cached_count {
res
} else {
let res = self.count_copies_uncached(original_cards);

COPIES_CACHE.lock().unwrap().insert(self.id, res);

res
}
}

#[inline]
fn count_copies_uncached(&self, original_cards: &[Card]) -> u32 {
if self.matches == 0 {
return 1;
}

(0..self.matches)
.into_par_iter()
.map(|i| self.id + i)
.map(|c| original_cards[c as usize].count_copies(original_cards))
.sum::<u32>()
+ 1
}
}

fn parse_cards(input: &str) -> Vec<Card> {
Expand Down Expand Up @@ -125,9 +95,7 @@ fn parse_card(input: &str) -> IResult<&str, Card> {
}

fn parse_int_list(input: &str) -> IResult<&str, Vec<u32>> {
let (input, ints) = separated_list0(space1, parse_int)(input)?;

Ok((input, ints))
separated_list0(space1, parse_int)(input)
}

fn parse_int(input: &str) -> IResult<&str, u32> {
Expand Down

0 comments on commit e8b4b67

Please sign in to comment.