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:
Stephen Waits committed 2020-12-15 13:58:14 -08:00
1 parent 9fb4f0de7c
commit 71a46593ba
4 files changed
+127

No files matched your search

+2
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
3789
667437230788118
+121
View File
@@ -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))
}
+2
View File
@@ -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(())