Skip to content

Commit e3296a3

Browse files
committed
Year 2018 Day 1
1 parent 2699473 commit e3296a3

File tree

7 files changed

+118
-0
lines changed

7 files changed

+118
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
6868
| [2021](#2021) | 10 |
6969
| [2020](#2020) | 286 |
7070
| [2019](#2019) | 22 |
71+
| [2018](#2018) | in progress |
7172
| [2017](#2017) | 515 |
7273
| [2016](#2016) | 663 |
7374
| [2015](#2015) | 87 |
@@ -232,6 +233,12 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
232233
| 24 | [Planet of Discord](https://adventofcode.com/2019/day/24) | [Source](src/year2019/day24.rs) | 139 |
233234
| 25 | [Cryostasis](https://adventofcode.com/2019/day/25) | [Source](src/year2019/day25.rs) | 2721 |
234235

236+
## 2018
237+
238+
| Day | Problem | Solution | Benchmark (μs) |
239+
| --- | --- | --- | --: |
240+
| 1 | [Chronal Calibration](https://adventofcode.com/2018/day/1) | [Source](src/year2018/day01.rs) | 15 |
241+
235242
## 2017
236243

237244
![pie-2017]

benches/benchmark.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ mod year2017 {
128128
benchmark!(year2017, day25);
129129
}
130130

131+
mod year2018 {
132+
benchmark!(year2018, day01);
133+
}
134+
131135
mod year2019 {
132136
benchmark!(year2019, day01);
133137
benchmark!(year2019, day02);

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ pub mod year2017 {
110110
pub mod day25;
111111
}
112112

113+
/// # Travel through time to restore the seasonal timeline.
114+
pub mod year2018 {
115+
pub mod day01;
116+
}
117+
113118
/// # Rescue Santa from deep space with a solar system adventure.
114119
pub mod year2019 {
115120
pub mod day01;

src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ fn main() {
2424
.chain(year2015())
2525
.chain(year2016())
2626
.chain(year2017())
27+
.chain(year2018())
2728
.chain(year2019())
2829
.chain(year2020())
2930
.chain(year2021())
@@ -176,6 +177,10 @@ fn year2017() -> Vec<Solution> {
176177
]
177178
}
178179

180+
fn year2018() -> Vec<Solution> {
181+
vec![solution!(year2018, day01)]
182+
}
183+
179184
fn year2019() -> Vec<Solution> {
180185
vec![
181186
solution!(year2019, day01),

src/year2018/day01.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! # Chronal Calibration
2+
//!
3+
//! The simplest approach to part two is to store previously seen numbers in a `HashSet` then
4+
//! stop once a duplicate is found. However this approach requires scanning the input of ~1,000
5+
//! numbers multiple times, around 150 times for my input.
6+
//!
7+
//! A much faster `O(nlogn)` approach relies on the fact that each frequency increases by the same
8+
//! amount (the sum of all deltas) each time the list of numbers is processed. For example:
9+
//!
10+
//! ```none
11+
//! Deltas: +1, -2, +3, +1 =>
12+
//! 0 1 -1 2
13+
//! 3 4 2 5
14+
//! ```
15+
//!
16+
//! Two frequencies that are a multiple of the sum will eventually repeat. First we group each
17+
//! frequencies by its remainder modulo the sum, using `rem_euclid` to handle negative frequencies
18+
//! correctly, Then we sort, first by the remainder to group frequencies that can repeat together,
19+
//! then by the frequency increasing in order to help find the smallest gap between similar
20+
//! frequencies, then lastly by index as this is needed in the next step.
21+
//!
22+
//! For the example this produces `[(0, 0, 0), (1, 1, 1), (2, -1, 2), (2, 2, 3)]`. Then we use
23+
//! a sliding windows of size two to compare each pair of adjacent canditates, considering only
24+
//! candidates with the same remainder. For each valid pair we then produce a tuple of
25+
//! `(frequency gap, index, frequency)`.
26+
//!
27+
//! Finally we sort the tuples in ascending order, first by smallest frequency gap, breaking any
28+
//! ties using the index to find frequencies that appear earlier in the list. The first tuple
29+
//! in the list gives the result, in the example this is `[(3, 2, 2)]`.
30+
use crate::util::parse::*;
31+
32+
pub fn parse(input: &str) -> Vec<i32> {
33+
input.iter_signed().collect()
34+
}
35+
36+
pub fn part1(input: &[i32]) -> i32 {
37+
input.iter().sum()
38+
}
39+
40+
pub fn part2(input: &[i32]) -> i32 {
41+
// The frequencies increase by this amount each pass through the list of deltas.
42+
let total: i32 = input.iter().sum();
43+
44+
// Calculate tuples of `(frequency gap, index, frequency)` then sort to group frequencies that
45+
// can collide together.
46+
let mut frequency: i32 = 0;
47+
let mut seen = Vec::with_capacity(input.len());
48+
49+
for n in input {
50+
seen.push((frequency.rem_euclid(total), frequency, seen.len()));
51+
frequency += n;
52+
}
53+
54+
seen.sort_unstable();
55+
56+
// Compare each adjacent pair of tuples to find candidates, then sort by smallest gap first,
57+
// tie breaking with index if needed.
58+
let mut pairs = Vec::new();
59+
60+
for window in seen.windows(2) {
61+
let (remainder0, freq0, index0) = window[0];
62+
let (remainder1, freq1, _) = window[1];
63+
64+
if remainder0 == remainder1 {
65+
pairs.push((freq1 - freq0, index0, freq1));
66+
}
67+
}
68+
69+
pairs.sort_unstable();
70+
71+
// Result is the frequency of the first tuple.
72+
let (_, _, freq) = pairs[0];
73+
freq
74+
}

tests/test.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ mod year2017 {
112112
mod day25_test;
113113
}
114114

115+
mod year2018 {
116+
mod day01_test;
117+
}
118+
115119
mod year2019 {
116120
mod day01_test;
117121
mod day02_test;

tests/year2018/day01_test.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use aoc::year2018::day01::*;
2+
3+
const EXAMPLE: &str = "\
4+
+1
5+
-2
6+
+3
7+
+1";
8+
9+
#[test]
10+
fn part1_test() {
11+
let input = parse(EXAMPLE);
12+
assert_eq!(part1(&input), 3);
13+
}
14+
15+
#[test]
16+
fn part2_test() {
17+
let input = parse(EXAMPLE);
18+
assert_eq!(part2(&input), 2);
19+
}

0 commit comments

Comments
 (0)