day 13 solution **after hints!**
Attempted a brute force solution. After letting my i9 churn on it across all cores and hyperthreads for ~4 days, looked online and learned about Chinese Remainder Theorem. Applied code from Rosetta Code and success. Signed-off-by: Stephen Waits <swaits@amazon.com>
This commit is contained in:
4 files changed
+127
No files matched your search
@@ -0,0 +1,2 @@
|
||||
1002392
|
||||
23,x,x,x,x,x,x,x,x,x,x,x,x,41,x,x,x,37,x,x,x,x,x,421,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,17,x,19,x,x,x,x,x,x,x,x,x,29,x,487,x,x,x,x,x,x,x,x,x,x,x,x,13
|
||||
@@ -0,0 +1,2 @@
|
||||
3789
|
||||
667437230788118
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// ==== day 13
|
||||
|
||||
use crate::utils;
|
||||
use anyhow::Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BusRoute {
|
||||
index: usize,
|
||||
time: usize,
|
||||
}
|
||||
|
||||
fn parse_bus_routes(input: &str) -> Vec<BusRoute> {
|
||||
input
|
||||
.trim()
|
||||
.split(',')
|
||||
.enumerate()
|
||||
.filter_map(|(i, s)| {
|
||||
if let Ok(t) = s.parse::<usize>() {
|
||||
Some(BusRoute { index: i, time: t })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn mins_to_next_departure(dep_time: usize, bus: &BusRoute) -> usize {
|
||||
let scheduled = bus.time * (dep_time / bus.time);
|
||||
if scheduled == dep_time {
|
||||
0
|
||||
} else {
|
||||
scheduled + bus.time - dep_time
|
||||
}
|
||||
}
|
||||
|
||||
// after failing at brute force, looked online for hints and learned about
|
||||
// the Chinese Remainder Theorem.
|
||||
//
|
||||
// code from https://rosettacode.org/wiki/Chinese_remainder_theorem#Rust
|
||||
fn egcd(a: i64, b: i64) -> (i64, i64, i64) {
|
||||
if a == 0 {
|
||||
(b, 0, 1)
|
||||
} else {
|
||||
let (g, x, y) = egcd(b % a, a);
|
||||
(g, y - (b / a) * x, x)
|
||||
}
|
||||
}
|
||||
|
||||
// after failing at brute force, looked online for hints and learned about
|
||||
// the Chinese Remainder Theorem.
|
||||
//
|
||||
// code from https://rosettacode.org/wiki/Chinese_remainder_theorem#Rust
|
||||
fn mod_inv(x: i64, n: i64) -> Option<i64> {
|
||||
let (g, x, _) = egcd(x, n);
|
||||
if g == 1 {
|
||||
Some((x % n + n) % n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// after failing at brute force, looked online for hints and learned about
|
||||
// the Chinese Remainder Theorem.
|
||||
//
|
||||
// code from https://rosettacode.org/wiki/Chinese_remainder_theorem#Rust
|
||||
fn chinese_remainder(residues: &[i64], modulii: &[i64]) -> Option<i64> {
|
||||
let prod = modulii.iter().product::<i64>();
|
||||
|
||||
let mut sum = 0;
|
||||
|
||||
for (&residue, &modulus) in residues.iter().zip(modulii) {
|
||||
let p = prod / modulus;
|
||||
sum += residue * mod_inv(p, modulus)? * p
|
||||
}
|
||||
|
||||
Some(sum % prod)
|
||||
}
|
||||
|
||||
// main
|
||||
pub fn run() -> Result<(usize, i64)> {
|
||||
let data = utils::read_file("data/input-13.txt")?;
|
||||
let answers = utils::read_i64s("data/output-13.txt")?;
|
||||
|
||||
// parse initial start time and bus routes
|
||||
let parsed: Vec<&str> = data.splitn(2, '\n').collect();
|
||||
assert_eq!(parsed.len(), 2);
|
||||
let departure_time = parsed[0].parse::<usize>().unwrap();
|
||||
let routes: Vec<BusRoute> = parse_bus_routes(parsed[1]);
|
||||
|
||||
// find which of the bus routes departs most soon after departure_time
|
||||
let mut next_departures: Vec<(usize, usize)> = routes
|
||||
.iter()
|
||||
.map(|bus| (mins_to_next_departure(departure_time, bus), bus.time))
|
||||
.collect();
|
||||
next_departures.sort_unstable();
|
||||
let best = next_departures.first().unwrap();
|
||||
let p1 = best.0 * best.1;
|
||||
|
||||
// use CRT to compute the congruence across all these modulii
|
||||
//
|
||||
// after failing at brute force, looked online for hints and learned about
|
||||
// the Chinese Remainder Theorem.
|
||||
//
|
||||
// code from https://rosettacode.org/wiki/Chinese_remainder_theorem#Rust
|
||||
let modulii: Vec<i64> = routes.iter().map(|bus| bus.time as i64).collect();
|
||||
let residues: Vec<i64> = routes
|
||||
.iter()
|
||||
.map(|bus| {
|
||||
if bus.index == 0 {
|
||||
0
|
||||
} else {
|
||||
((100 * bus.time - bus.index) % bus.time) as i64
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let p2 = chinese_remainder(&residues, &modulii).unwrap();
|
||||
|
||||
assert_eq!(p1, answers[0] as usize);
|
||||
assert_eq!(p2, answers[1]);
|
||||
Ok((p1, p2))
|
||||
}
|
||||
@@ -12,6 +12,7 @@ mod day09;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
mod day13;
|
||||
mod day14;
|
||||
mod day15;
|
||||
mod utils;
|
||||
@@ -39,6 +40,7 @@ fn main() -> Result<()> {
|
||||
println!("Day 10: {:?}", day10::run()?);
|
||||
println!("Day 11: {:?}", day11::run()?);
|
||||
println!("Day 12: {:?}", day12::run()?);
|
||||
println!("Day 13: {:?}", day13::run()?);
|
||||
println!("Day 14: {:?}", day14::run()?);
|
||||
println!("Day 15: {:?}", day15::run()?);
|
||||
Ok(())
|
||||
|
||||
Reference in new issue
Block a user