π Advent of Code 2'023 - Day 09
Day 09: Mirage Maintenance
* Cf. aoc. d. ix xxiii
Abridged Problem Description: Given a dataset of numbers, we are required to predict the next number in the sequence.
Parsing The Input
First, Iβd like to have a line-by-line vector<string> of the input. Then I can
parse it further.
vector<string> input = Utils::split_lines(Utils::read_file("input/day_09_input"));
To parse every line into a vector<int64>, we can do the following:
vector<vector<int64>> day_09_input(input.size(), vector<int64>());
for (auto &n : input) day_09_input.push_back(Utils::parse_v_numbers(n));
Bene Nota: Iβm ignoring the first line because that is our instructions.
PARS I
The first part is straightforward. We need to get the last number of the input after
each iteration, also taking into account that the result is always n - 1.
int64 sum = 0;
for (vector<int64> data : lines) {
do {
if (data.empty()) continue;
sum += data.back();
std::adjacent_difference(data.begin(), data.end(), data.begin());
data.erase(data.begin());
} while (!std::all_of(data.begin(), data.end(), [](auto d) { return d == 0; }));
}
return sum;
That was an easy star π
PARS II
For the second part, we need to predict the previous number in the sequence instead of the next one. This requires a slight modification to our approach.
Ξ±
The key insight is that we need to work backwards through the difference sequences. Instead of adding the last number of each level, we need to subtract the first number of each level from the previous value.
int64 sum = 0;
for (vector<int64> data : lines) {
vector<vector<int64>> sequences;
sequences.push_back(data);
// Build difference sequences
while (true) {
vector<int64> diff;
for (size_t i = 1; i < data.size(); ++i) {
diff.push_back(data[i] - data[i-1]);
}
sequences.push_back(diff);
data = diff;
if (all_of(diff.begin(), diff.end(), [](auto d) { return d == 0; })) {
break;
}
}
// Extrapolate backwards
int64 prev_value = 0;
for (int i = sequences.size() - 2; i >= 0; --i) {
prev_value = sequences[i][0] - prev_value;
}
sum += prev_value;
}
return sum;
Ξ²
The algorithm works by:
- Building the same difference sequences as in Part 1
- Starting from the all-zeros sequence with
prev_value = 0 - For each level going backwards, we calculate
prev_value = first_number - prev_value - The final
prev_valueis the predicted previous number in the original sequence
Hereβs the complete solution:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
class Solution_09 {
public:
static int64 part1(const vector<vector<int64>>& lines) {
int64 sum = 0;
for (vector<int64> data : lines) {
do {
if (data.empty()) continue;
sum += data.back();
std::adjacent_difference(data.begin(), data.end(), data.begin());
data.erase(data.begin());
} while (!std::all_of(data.begin(), data.end(), [](auto d) { return d == 0; }));
}
return sum;
}
static int64 part2(const vector<vector<int64>>& lines) {
int64 sum = 0;
for (vector<int64> data : lines) {
vector<vector<int64>> sequences;
sequences.push_back(data);
// Build difference sequences
while (true) {
vector<int64> diff;
for (size_t i = 1; i < data.size(); ++i) {
diff.push_back(data[i] - data[i-1]);
}
sequences.push_back(diff);
data = diff;
if (all_of(diff.begin(), diff.end(), [](auto d) { return d == 0; })) {
break;
}
}
// Extrapolate backwards
int64 prev_value = 0;
for (int i = sequences.size() - 2; i >= 0; --i) {
prev_value = sequences[i][0] - prev_value;
}
sum += prev_value;
}
return sum;
}
};
Awesome! We solved the spooky puzzle! ππ
Click here to access the blogpost from @fefas
Ξ±) The key difference between Part 1 and Part 2 is the direction of extrapolation. In Part 1, we work with the last elements and add them up. In Part 2, we work with the first elements and subtract them in reverse order.
Ξ²) This algorithm is based on the mathematical concept of finite differences, which is commonly used in numerical analysis and polynomial interpolation. The process of computing differences until we reach all zeros is equivalent to finding the degree of the polynomial that fits the sequence.