-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathday17.rs
More file actions
77 lines (66 loc) · 2.43 KB
/
day17.rs
File metadata and controls
77 lines (66 loc) · 2.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! # Spinlock
//!
//! For part one, we simulate 2017 rounds and record the position (index) at which each number
//! is inserted. We then find the index after the number 2017. Finally, we iterate backwards
//! through the stored indexes to find the first (i.e. last) number inserted at that index.
//!
//! There are two insights that speed up part two.
//!
//! The first is that we don't need a buffer. We only need to preserve the last value inserted
//! whenever the index becomes zero. Once 50 million values have been inserted then this value
//! is the final result.
//!
//! The second trick is realizing that we can insert multiple values at a time before the index
//! wraps around. For example, if the index is 1, the current value 10,000 and the step 300,
//! then we can insert 34 values at once. The [`div_ceil`] method is perfect for this computation.
//!
//! The number of skips grows geometrically, or `O(ln n)` overall effort, learning the answer in
//! just a few thousand iterations.
//!
//! [`div_ceil`]: usize::div_ceil
use crate::util::parse::*;
type Input = (usize, usize);
pub fn parse(input: &str) -> Input {
let step = input.unsigned::<usize>() + 1;
// For part one, track the index every node had when inserted.
let mut index = 0;
let indices: Vec<_> = (1..=2017)
.map(|n| {
index = (index + step) % n;
index
})
.collect();
// Now back up to find the prior node that shares the same index, accounting for when
// the index moved because an intermediate number was assigned an earlier index.
let mut next = (index + 1) % 2017;
let mut part_one = 0;
for (i, &o) in indices.iter().enumerate().rev() {
if o == next {
part_one = i + 1;
break;
}
if o < next {
next -= 1;
}
}
// For part two, we only need to focus on nodes inserted at index 0.
let mut n = 2017;
let mut part_two = 0;
while n <= 50_000_000 {
if index == 0 {
part_two = n;
}
let skip = (n - index).div_ceil(step - 1);
n += skip;
// Here, n is larger than 2017, while step is on the order of 300 to 400; therefore, step
// wraps only once, and subtraction works instead of modulus.
index = index + skip * step - n;
}
(part_one, part_two)
}
pub fn part1(input: &Input) -> usize {
input.0
}
pub fn part2(input: &Input) -> usize {
input.1
}