This commit is contained in:
Andrew Glaze 2023-12-27 10:20:20 -05:00
parent bdc150d0ed
commit 14c6da72d9
2 changed files with 145 additions and 0 deletions

144
src/day24.rs Normal file
View File

@ -0,0 +1,144 @@
use std::ops::RangeInclusive;
use aoc_runner_derive::{aoc, aoc_generator};
//const RANGE: RangeInclusive<f64> = 7.0..=27.0;
const RANGE: RangeInclusive<f64> = 200_000_000_000_000.0..=400_000_000_000_000.0;
#[aoc_generator(day24)]
fn parse(input: &str) -> Vec<[f64; 6]> {
let input: Vec<[f64; 6]> = input.lines()
.map(|line| {
line.split("@")
.flat_map(|components| {
components.split(",")
.map(str::trim)
.map(|num| num.parse::<f64>().unwrap())
})
.collect::<Vec<_>>().try_into().unwrap()
}).collect::<Vec<_>>();
input
}
#[aoc(day24, part1)]
fn part1(input: &[[f64; 6]]) -> u32 {
let mut total = 0;
// It's just solve the linear equations. I'm using a matrix.
for i in 1..input.len() {
for j in 0..i {
let [a, b, _, d, e, _] = input[i];
let [g, h, _, j, k, _] = input[j];
let determinant = e * j - d * k;
if determinant == 0.0 {
continue;
}
let t = (j * (h - b) - k * (g - a)) / determinant;
let u = (d * (h - b) - e * (g - a)) / determinant;
let x = a + t * d;
let y = b + t * e;
if t >= 0.0 && u >= 0.0 && RANGE.contains(&x) && RANGE.contains(&y) {
total += 1;
}
}
}
total
}
#[aoc(day24, part2)]
fn part2(input: &[[f64; 6]]) -> f64 {
let [a, b, c, d, e, f] = input[0];
let [g, h, i, j, k, l] = input[1];
let [m, n, o, p, q, r] = input[2];
let mut matrix = [
[0.0, l - f, e - k, 0.0, c - i, h - b, e * c - b * f + h * l - k * i],
[0.0, r - f, e - q, 0.0, c - o, n - b, e * c - b * f + n * r - q * o],
[f - l, 0.0, j - d, i - c, 0.0, a - g, a * f - d * c + j * i - g * l],
[f - r, 0.0, p - d, o - c, 0.0, a - m, a * f - d * c + p * o - m * r],
[k - e, d - j, 0.0, b - h, g - a, 0.0, d * b - a * e + g * k - j * h],
[q - e, d - p, 0.0, b - n, m - a, 0.0, d * b - a * e + m * q - p * n],
];
// Use Gaussian elimination to solve for the 6 unknowns.
// Forward elimination, processing columns from left to right.
// This will leave a matrix in row echelon form.
// That's right, I got a D in applied linear algebra.
for pivot in 0..6 {
// Make leading coefficient of each row positive to make subsequent calculations easier.
for row in &mut matrix[pivot..] {
if row[pivot] < 0.0 {
row.iter_mut().for_each(|num| *num = -*num);
}
}
loop {
let column = matrix.map(|row| row[pivot]);
// If only one non-zero coefficient remaining in the column then we're done.
if column[pivot..].iter().filter(|&&c| c > 0.0).count() == 1 {
// Move this row into the pivot location
let index = column.iter().rposition(|&c| c > 0.0).unwrap();
matrix.swap(pivot, index);
break;
}
// Find the row with the lowest non-zero leading coefficient.
let min = *column[pivot..].iter().filter(|&&c| c > 0.0).min_by(|a, b| a.total_cmp(b)).unwrap();
let index = column.iter().rposition(|&c| c == min).unwrap();
// Subtract as many multiples of this minimum row from each other row as possible
// to shrink the coefficients of our column towards zero.
for row in pivot..6 {
if row != index && column[row] != 0.0 {
let factor = column[row] / min;
for col in pivot..7 {
matrix[row][col] -= factor * matrix[index][col];
}
}
}
}
}
// Back substitution, processing columns from right to left.
// This will leave the matrix in reduced row echelon form.
// The solved unknowns are then in the 7th column.
for pivot in (0..6).rev() {
matrix[pivot][6] /= matrix[pivot][pivot];
for row in 0..pivot {
matrix[row][6] -= matrix[pivot][6] * matrix[row][pivot];
}
}
(matrix[0][6] + matrix[1][6] + matrix[2][6]).floor()
}
#[cfg(test)]
mod tests {
use super::*;
const EX: &str = r"19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3";
#[test]
fn part1_example() {
assert_eq!(part1(&parse(EX)), 2);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse(EX)), 47.0);
}
}

View File

@ -1,3 +1,4 @@
mod day24;
mod day23;
mod day22;
mod day21;