Skip to content

Commit

Permalink
Year 2018 Day 1
Browse files Browse the repository at this point in the history
  • Loading branch information
maneatingape committed Jul 15, 2024
1 parent 2699473 commit 3a27b90
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
| [2021](#2021) | 10 |
| [2020](#2020) | 286 |
| [2019](#2019) | 22 |
| [2018](#2018) | in progress |
| [2017](#2017) | 515 |
| [2016](#2016) | 663 |
| [2015](#2015) | 87 |
Expand Down Expand Up @@ -232,6 +233,12 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
| 24 | [Planet of Discord](https://adventofcode.com/2019/day/24) | [Source](src/year2019/day24.rs) | 139 |
| 25 | [Cryostasis](https://adventofcode.com/2019/day/25) | [Source](src/year2019/day25.rs) | 2721 |

## 2018

| Day | Problem | Solution | Benchmark (μs) |
| --- | --- | --- | --: |
| 1 | [Chronal Calibration](https://adventofcode.com/2018/day/1) | [Source](src/year2018/day01.rs) | 15 |

## 2017

![pie-2017]
Expand Down
4 changes: 4 additions & 0 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ mod year2017 {
benchmark!(year2017, day25);
}

mod year2018 {
benchmark!(year2018, day01);
}

mod year2019 {
benchmark!(year2019, day01);
benchmark!(year2019, day02);
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ pub mod year2017 {
pub mod day25;
}

/// # Travel through time to restore the seasonal timeline.
pub mod year2018 {
pub mod day01;
}

/// # Rescue Santa from deep space with a solar system adventure.
pub mod year2019 {
pub mod day01;
Expand Down
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn main() {
.chain(year2015())
.chain(year2016())
.chain(year2017())
.chain(year2018())
.chain(year2019())
.chain(year2020())
.chain(year2021())
Expand Down Expand Up @@ -176,6 +177,10 @@ fn year2017() -> Vec<Solution> {
]
}

fn year2018() -> Vec<Solution> {
vec![solution!(year2018, day01)]
}

fn year2019() -> Vec<Solution> {
vec![
solution!(year2019, day01),
Expand Down
75 changes: 75 additions & 0 deletions src/year2018/day01.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! # Chronal Calibration
//!
//! The simplest approach to part two is to store previously seen numbers in a `HashSet` then
//! stop once a duplicate is found. However this approach requires scanning the input of ~1,000
//! numbers multiple times, around 150 times for my input.
//!
//! A much faster `O(nlogn)` approach relies on the fact that each frequency increases by the same
//! amount (the sum of all deltas) each time the list of numbers is processed. For example:
//!
//! ```none
//! Deltas: +1, -2, +3, +1 =>
//! 0 1 -1 2
//! 3 4 2 5
//! ```
//!
//! Two frequencies that are a multiple of the sum will eventually repeat. First we group each
//! frequencies by its remainder modulo the sum, using `rem_euclid` to handle negative frequencies
//! correctly, Then we sort, first by the remainder to group frequencies that can repeat together,
//! then by the frequency increasing in order to help find the smallest gap between similar
//! frequencies, then lastly by index as this is needed in the next step.
//!
//! For the example this produces `[(0, 0, 0), (1, 1, 1), (2, -1, 2), (2, 2, 3)]`. Then we use
//! a sliding windows of size two to compare each pair of adjacent canditates, considering only
//! candidates with the same remainder. For each valid pair we then produce a tuple of
//! `(frequency gap, index, frequency)`.
//!
//! Finally we sort the tuples in ascending order, first by smallest frequency gap, breaking any
//! ties using the index to find frequencies that appear earlier in the list. The first tuple
//! in the list gives the result, in the example this is `[(3, 2, 2)]`.
use crate::util::parse::*;

pub fn parse(input: &str) -> Vec<i32> {
input.iter_signed().collect()
}

pub fn part1(input: &[i32]) -> i32 {
input.iter().sum()
}

pub fn part2(input: &[i32]) -> i32 {
// The frequencies increase by this amount each pass through the list of deltas.
let total: i32 = input.iter().sum();

// Calculate tuples of `(frequency gap, index, frequency)` then sort to group frequencies that
// can collide together.
let mut frequency: i32 = 0;
let mut seen = Vec::with_capacity(input.len());

for n in input {
seen.push((frequency.rem_euclid(total), frequency, seen.len()));
frequency += n;
}

seen.sort_unstable();

// Compare each adjacent pair of tuples to find candidates, then sort by smallest gap first,
// tie breaking with index if needed.
let mut pairs = Vec::new();

for window in seen.windows(2) {
let (remainder0, freq0, index0) = window[0];
let (remainder1, freq1, _) = window[1];

if remainder0 == remainder1 {
pairs.push((freq1 - freq0, index0, freq1));
}
}

pairs.sort_unstable();
println!("{:?}", pairs);

// Result is the frequency of the first tuple.
let (_, _, freq) = pairs[0];
freq
}
4 changes: 4 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ mod year2017 {
mod day25_test;
}

mod year2018 {
mod day01_test;
}

mod year2019 {
mod day01_test;
mod day02_test;
Expand Down
19 changes: 19 additions & 0 deletions tests/year2018/day01_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use aoc::year2018::day01::*;

const EXAMPLE: &str = "\
+1
-2
+3
+1";

#[test]
fn part1_test() {
let input = parse(EXAMPLE);
assert_eq!(part1(&input), 3);
}

#[test]
fn part2_test() {
let input = parse(EXAMPLE);
assert_eq!(part2(&input), 2);
}

0 comments on commit 3a27b90

Please sign in to comment.