Skip to content

Commit ba96284

Browse files
authored
Merge pull request #155 from romellem/2019/day-16
2019 - Day 16
2 parents 02ade0b + 2ef5fcd commit ba96284

File tree

7 files changed

+310
-2
lines changed

7 files changed

+310
-2
lines changed

2019/16/README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Answers
2+
3+
| Part 1 | Part 2 |
4+
|------------|------------|
5+
| `27831665` | `36265589` |
6+
7+
## --- Day 16: Flawed Frequency Transmission ---
8+
9+
You're 3/4ths of the way through the [gas giants](https://en.wikipedia.org/wiki/Gas_giant). Not only do roundtrip signals to Earth take five hours, but the signal quality is quite bad as well. You can clean up the signal with the Flawed Frequency Transmission algorithm, or _FFT_.
10+
11+
As input, FFT takes a list of numbers. In the signal you received (your puzzle input), each number is a single digit: data like `15243` represents the sequence `1`, `5`, `2`, `4`, `3`.
12+
13+
FFT operates in repeated _phases_. In each phase, a new list is constructed with the same length as the input list. This new list is also used as the input for the next phase.
14+
15+
Each element in the new list is built by multiplying every value in the input list by a value in a repeating _pattern_ and then adding up the results. So, if the input list were `9, 8, 7, 6, 5` and the pattern for a given element were `1, 2, 3`, the result would be `9*1 + 8*2 + 7*3 + 6*1 + 5*2` (with each input element on the left and each value in the repeating pattern on the right of each multiplication). Then, only the ones digit is kept: `38` becomes `8`, `-17` becomes `7`, and so on.
16+
17+
While each element in the output array uses all of the same input array elements, the actual repeating pattern to use depends on _which output element_ is being calculated. The base pattern is `0, 1, 0, -1`. Then, repeat each value in the pattern a number of times equal to the _position in the output list_ being considered. Repeat once for the first element, twice for the second element, three times for the third element, and so on. So, if the third element of the output list is being calculated, repeating the values would produce: `0, 0, 0, 1, 1, 1, 0, 0, 0, -1, -1, -1`.
18+
19+
When applying the pattern, skip the very first value exactly once. (In other words, offset the whole pattern left by one.) So, for the second element of the output list, the actual pattern used would be: `0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0, -1, -1, ...`.
20+
21+
After using this process to calculate each element of the output list, the phase is complete, and the output list of this phase is used as the new input list for the next phase, if any.
22+
23+
Given the input signal `12345678`, below are four phases of FFT. Within each phase, each output digit is calculated on a single line with the result at the far right; each multiplication operation shows the input digit on the left and the pattern value on the right:
24+
25+
Input signal: 12345678
26+
27+
1*1 + 2*0 + 3*-1 + 4*0 + 5*1 + 6*0 + 7*-1 + 8*0 = 4
28+
1*0 + 2*1 + 3*1 + 4*0 + 5*0 + 6*-1 + 7*-1 + 8*0 = 8
29+
1*0 + 2*0 + 3*1 + 4*1 + 5*1 + 6*0 + 7*0 + 8*0 = 2
30+
1*0 + 2*0 + 3*0 + 4*1 + 5*1 + 6*1 + 7*1 + 8*0 = 2
31+
1*0 + 2*0 + 3*0 + 4*0 + 5*1 + 6*1 + 7*1 + 8*1 = 6
32+
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*1 + 7*1 + 8*1 = 1
33+
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*0 + 7*1 + 8*1 = 5
34+
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*0 + 7*0 + 8*1 = 8
35+
36+
After 1 phase: 48226158
37+
38+
4*1 + 8*0 + 2*-1 + 2*0 + 6*1 + 1*0 + 5*-1 + 8*0 = 3
39+
4*0 + 8*1 + 2*1 + 2*0 + 6*0 + 1*-1 + 5*-1 + 8*0 = 4
40+
4*0 + 8*0 + 2*1 + 2*1 + 6*1 + 1*0 + 5*0 + 8*0 = 0
41+
4*0 + 8*0 + 2*0 + 2*1 + 6*1 + 1*1 + 5*1 + 8*0 = 4
42+
4*0 + 8*0 + 2*0 + 2*0 + 6*1 + 1*1 + 5*1 + 8*1 = 0
43+
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*1 + 5*1 + 8*1 = 4
44+
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*0 + 5*1 + 8*1 = 3
45+
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*0 + 5*0 + 8*1 = 8
46+
47+
After 2 phases: 34040438
48+
49+
3*1 + 4*0 + 0*-1 + 4*0 + 0*1 + 4*0 + 3*-1 + 8*0 = 0
50+
3*0 + 4*1 + 0*1 + 4*0 + 0*0 + 4*-1 + 3*-1 + 8*0 = 3
51+
3*0 + 4*0 + 0*1 + 4*1 + 0*1 + 4*0 + 3*0 + 8*0 = 4
52+
3*0 + 4*0 + 0*0 + 4*1 + 0*1 + 4*1 + 3*1 + 8*0 = 1
53+
3*0 + 4*0 + 0*0 + 4*0 + 0*1 + 4*1 + 3*1 + 8*1 = 5
54+
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*1 + 3*1 + 8*1 = 5
55+
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*0 + 3*1 + 8*1 = 1
56+
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*0 + 3*0 + 8*1 = 8
57+
58+
After 3 phases: 03415518
59+
60+
0*1 + 3*0 + 4*-1 + 1*0 + 5*1 + 5*0 + 1*-1 + 8*0 = 0
61+
0*0 + 3*1 + 4*1 + 1*0 + 5*0 + 5*-1 + 1*-1 + 8*0 = 1
62+
0*0 + 3*0 + 4*1 + 1*1 + 5*1 + 5*0 + 1*0 + 8*0 = 0
63+
0*0 + 3*0 + 4*0 + 1*1 + 5*1 + 5*1 + 1*1 + 8*0 = 2
64+
0*0 + 3*0 + 4*0 + 1*0 + 5*1 + 5*1 + 1*1 + 8*1 = 9
65+
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*1 + 1*1 + 8*1 = 4
66+
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*0 + 1*1 + 8*1 = 9
67+
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*0 + 1*0 + 8*1 = 8
68+
69+
After 4 phases: 01029498
70+
71+
72+
Here are the first eight digits of the final output list after 100 phases for some larger inputs:
73+
74+
* `80871224585914546619083218645595` becomes `24176176`.
75+
* `19617804207202209144916044189917` becomes `73745418`.
76+
* `69317163492948606335995924319873` becomes `52432133`.
77+
78+
After _100_ phases of FFT, _what are the first eight digits in the final output list?_
79+
80+
-----------------
81+
82+
## --- Part Two ---
83+
84+
Now that your FFT is working, you can decode the _real signal_.
85+
86+
The real signal is your puzzle input _repeated 10000 times_. Treat this new signal as a single input list. Patterns are still calculated as before, and 100 phases of FFT are still applied.
87+
88+
The _first seven digits_ of your initial input signal also represent the _message offset_. The message offset is the location of the eight-digit message in the final output list. Specifically, the message offset indicates _the number of digits to skip_ before reading the eight-digit message. For example, if the first seven digits of your initial input signal were `1234567`, the eight-digit message would be the eight digits after skipping 1,234,567 digits of the final output list. Or, if the message offset were `7` and your final output list were `98765432109876543210`, the eight-digit message would be `21098765`. (Of course, your real message offset will be a seven-digit number, not a one-digit number like `7`.)
89+
90+
Here is the eight-digit message in the final output list after 100 phases. The message offset given in each input has been highlighted. (Note that the inputs given below are repeated 10000 times to find the actual starting input lists.)
91+
92+
* <code><b>0303673</b>2577212944063491565474664</code> becomes `84462026`.
93+
* <code><b>0293510</b>9699940807407585447034323</code> becomes `78725270`.
94+
* <code><b>0308177</b>0884921959731165446850517</code> becomes `53553731`.
95+
96+
After repeating your input signal 10000 times and running 100 phases of FFT, _what is the eight-digit message embedded in the final output list?_

2019/16/input.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
4+
const input = fs
5+
.readFileSync(path.join(__dirname, 'input.txt'), 'utf8')
6+
.toString()
7+
.trim()
8+
.split('')
9+
.map(v => +v);
10+
11+
const inputTimes10 = [];
12+
for (let i = 0; i < 10; i++) {
13+
inputTimes10.push(...input);
14+
}
15+
16+
const partTwoInput = [];
17+
for (let i = 0; i < 10000; i++) {
18+
partTwoInput.push(...input);
19+
}
20+
21+
module.exports = {
22+
input,
23+
inputTimes10,
24+
partTwoInput,
25+
};

2019/16/input.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
59766977873078199970107568349014384917072096886862753001181795467415574411535593439580118271423936468093569795214812464528265609129756216554981001419093454383882560114421882354033176205096303121974045739484366182044891267778931831792562035297585485658843180220796069147506364472390622739583789825303426921751073753670825259141712329027078263584903642919122991531729298497467435911779410970734568708255590755424253797639255236759229935298472380039602200033415155467240682533288468148414065641667678718893872482168857631352275667414965503393341925955626006552556064728352731985387163635634298416016700583512112158756656289482437803808487304460165855189

2019/16/part-one-optimized.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Performs much better for larger inputs because:
2+
// - We don't generate long arrays for patterns
3+
const { input } = require('./input');
4+
5+
const base_pattern = [0, 1, 0, -1];
6+
let current = input;
7+
8+
for (let phase = 1; phase <= 100; phase++) {
9+
let new_arr = [];
10+
for (let n = 0; n < current.length; n++) {
11+
let sum = 0;
12+
13+
let index = 0;
14+
let position = n + 1;
15+
for (let i = 0; i < current.length; i++) {
16+
position--;
17+
if (position <= 0) {
18+
position = n + 1;
19+
index = (index + 1) % base_pattern.length;
20+
}
21+
22+
let v = current[i];
23+
let scalar = base_pattern[index];
24+
let new_val = v * scalar;
25+
26+
sum += new_val;
27+
}
28+
29+
let last_digit = Math.abs(sum % 10);
30+
31+
new_arr.push(last_digit);
32+
}
33+
34+
current = new_arr;
35+
}
36+
37+
let first_eight_digits = current.slice(0, 8).join('');
38+
console.log(first_eight_digits);

2019/16/part-one.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { input } = require('./input');
2+
3+
var fillPattern = (function () {
4+
let cachedPatterns = {};
5+
6+
return (n) => {
7+
if (cachedPatterns[n]) {
8+
return cachedPatterns[n];
9+
}
10+
11+
let pattern = [];
12+
for (let num of [0, 1, 0, -1]) {
13+
for (let i = 0; i < n; i++) {
14+
pattern.push(num);
15+
}
16+
}
17+
18+
// Offset the whole pattern left by one
19+
let first_digit = pattern.shift();
20+
pattern.push(first_digit);
21+
22+
cachedPatterns[n] = pattern;
23+
24+
return pattern;
25+
};
26+
})();
27+
28+
let current = input;
29+
30+
for (let phase = 1; phase <= 100; phase++) {
31+
let new_arr = [];
32+
for (let n = 0; n < current.length; n++) {
33+
let pattern = fillPattern(n + 1);
34+
let sum = 0;
35+
for (let i = 0; i < current.length; i++) {
36+
let v = current[i];
37+
let index = i % pattern.length;
38+
let new_val = v * pattern[index];
39+
40+
sum += new_val;
41+
}
42+
43+
let last_char = String(sum).substr(-1, 1);
44+
let last_digit = parseInt(last_char, 10);
45+
46+
new_arr.push(last_digit);
47+
}
48+
49+
current = new_arr;
50+
}
51+
52+
let first_eight_digits = current.slice(0, 8).join('');
53+
console.log(first_eight_digits);

2019/16/part-two.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const { partTwoInput } = require('./input');
2+
3+
const offset = parseInt(partTwoInput.slice(0, 7).join(''), 10);
4+
const slice_to_iterate = partTwoInput.slice(offset);
5+
for (let j = 0; j < 100; j++) {
6+
// We don't need to do anything with the last number so start at 2nd the last
7+
for (let i = slice_to_iterate.length - 2; i >= 0; i--) {
8+
slice_to_iterate[i] = (slice_to_iterate[i + 1] + slice_to_iterate[i]) % 10;
9+
}
10+
}
11+
12+
// First 8 digits from our offset
13+
console.log(slice_to_iterate.slice(0, 8).join(''));
14+
15+
/*
16+
17+
Here's the trick:
18+
19+
Let's say your 12 digits number is `123456789123`.
20+
Look at the table after you get past the "halfway point" down the list:
21+
22+
| # | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 | 3 |
23+
|----|------|------|------|------|------|------|------|------|------|------|------|------|
24+
| 1 | 1 | | -3 | | 5 | | -7 | | 9 | | -2 | |
25+
| 2 | | 2 | 3 | | | -6 | -7 | | | 1 | 2 | |
26+
| 3 | | | 3 | 4 | 5 | | | | -9 | -1 | -2 | |
27+
| 4 | | | | 4 | 5 | 6 | 7 | | | | | -3 |
28+
| 5 | | | | | 5 | 6 | 7 | 8 | 9 | | | |
29+
| 6 | | | | | | 6 | 7 | 8 | 9 | 1 | 2 | |
30+
| 7 | | | | | | | *7* | *8* | *9* | *1* | *2* | *3* |
31+
| 8 | | | | | | | | *8* | *9* | *1* | *2* | *3* |
32+
| 9 | | | | | | | | | *9* | *1* | *2* | *3* |
33+
| 10 | | | | | | | | | | *1* | *2* | *3* |
34+
| 11 | | | | | | | | | | | *2* | *3* |
35+
| 12 | | | | | | | | | | | | *3* |
36+
37+
Starting from row 7 on down, all the numbers are added together! No 0s, no -1s, just the numbers
38+
added.
39+
40+
Since our "offset" is more than halfway through our full list, we can just immediately jump to the offset
41+
and compute those numbers on down.
42+
43+
Now adding up all those numbers in the triangle is still a _lot._ But there is another trick!
44+
45+
Look at the very last number, 3. let's call it A.
46+
47+
Go up one row, we have 2 + (3). But wait, 3 is A. So we have 2 + A. Let's call that B.
48+
49+
Go up another row, we have 1 + 2 + (3) -> 1 + (2 + A) -> 1 + B. Call that C.
50+
51+
You see where this is going. Go up another row, 9 + 1 + 2 + (3),
52+
9 + 1 + (2 + A) -> 9 + (1 + B) -> 9 + C. Call that D.
53+
54+
And so on.
55+
56+
The other trick is, we only need to store the last digit each time we start from the bottom
57+
and loop up.
58+
59+
Consider
60+
61+
n + ... = x_n n + x_n-1 = x_n
62+
d + c + b + a = x_4 d + x_3 = x_4
63+
c + b + a = x_3 => c + x_2 = x_3
64+
b + a = x_2 b + x_1 = x_2
65+
a = x_1 a = x_1
66+
67+
Let `x_n` = `D_i..D_1D_0`, where `D_n` is a single digit.
68+
69+
Clearly `(x_n)mod 10 = D_0`, aka the last digit.
70+
71+
Let `l + x_n` be the next line up we want to add. So `l` is a single digit number.
72+
73+
I'm saying we should only care about the last digit of all that addition, so I want to show that
74+
75+
(l + x_n)mod 10 = (l + D_0)mod 10.
76+
77+
From the "Addition Property" of modular arithmetic, we know that
78+
79+
(a + b)mod_n = [(a)mod_n + (b)mod_n]mod_n
80+
81+
So, expanding `(l + x_n)mod 10` is
82+
83+
(l + x_n)mod_10 = ((l)mod_10 + (x_n)mod_10)mod_10.
84+
85+
# Well `(l)mod_10` is just `l`, since `l` is less than 10 (aka a single digit).
86+
87+
(l + x_n)mod_10 = ((l) + (x_n)mod_10)mod_10.
88+
89+
# Add (x_n)mod_10 = D_0
90+
91+
(l + x_n)mod_10 = (l + D0 )mod_10.
92+
93+
QED ∎
94+
95+
*/

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Completed: **25 / 25** (100% ⭐️)
4949

5050
## 2019 Puzzles List
5151

52-
Completed: **16 / 25** (64%)
52+
Completed: **17 / 25** (68%)
5353

5454
* [✅ Day 1](2019/1/)
5555
* [✅ Day 2](2019/2/)
@@ -66,7 +66,7 @@ Completed: **16 / 25** (64%)
6666
* [✅ Day 13](2019/13/)
6767
* [✅ Day 14](2019/14/)
6868
* Day 15
69-
* Day 16
69+
* [Day 16](2019/16/)
7070
* Day 17
7171
* Day 18
7272
* [✅ Day 19](2019/19/)

0 commit comments

Comments
 (0)