From 3c1a48fe51eb63d2a658f9d242ec0010c60c717a Mon Sep 17 00:00:00 2001 From: Johannes Date: Sat, 8 Oct 2022 17:05:14 +0200 Subject: [PATCH] Day 22 part 2 --- Cargo.lock | 73 ++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 1 + src/main.rs | 2 +- src/tasks/day22.rs | 76 ++++++++++++++++++++++++++++++---------------- 4 files changed, 115 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58e521b..68d8384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,10 +15,11 @@ dependencies = [ name = "aoc_2019" version = "0.1.0" dependencies = [ - "itertools", - "modinverse", - "num-integer", - "regex", + "itertools", + "mod_exp", + "modinverse", + "num-integer", + "regex", ] [[package]] @@ -39,7 +40,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ - "either", + "either", ] [[package]] @@ -48,13 +49,45 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "mod_exp" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4a3fad6af79fcf8d1417b2f6359d814a173fb413a1bd48ba04208b42b0d8b0" +dependencies = [ + "num", +] + [[package]] name = "modinverse" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f62f577f148cc1a9466e7065a22e59466a7d537cceba5e77e57181d0f706633" dependencies = [ - "num-integer", + "num-integer", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", ] [[package]] @@ -63,8 +96,30 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg", - "num-traits", + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", ] [[package]] @@ -73,7 +128,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg", + "autocfg", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 98324e5..124c5b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ itertools = "0.10.3" regex = "1.5.5" num-integer = "0.1.44" modinverse = "0.1.1" +mod_exp = "1.0.1" diff --git a/src/main.rs b/src/main.rs index 04c45b6..d9a6067 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,5 +3,5 @@ extern crate core; mod tasks; fn main() { - tasks::day16::run(); + tasks::day22::run(); } diff --git a/src/tasks/day22.rs b/src/tasks/day22.rs index eb5c339..7159d7c 100644 --- a/src/tasks/day22.rs +++ b/src/tasks/day22.rs @@ -1,16 +1,20 @@ -use modinverse::modinverse; -use std::collections::HashMap; use std::collections::VecDeque; +use mod_exp::mod_exp; +use modinverse::modinverse; + +use crate::tasks::day22::Cmd::{Cut, Deal, Stack}; + pub fn run() { let input = std::fs::read_to_string("input/day22.txt").unwrap(); - println!("{}", task2(input, 119315717514047, 2020, 101741582076661)); + println!("Task 1: {}", task1(&input)); + println!("Task 2: {}", task2(&input)); } type Deck = VecDeque; type Size = i128; -fn task1(input: String) -> usize { +fn task1(input: &str) -> usize { let deck_size = 10_007; let mut deck: Deck = (0..deck_size).collect(); for line in input.lines() { @@ -59,31 +63,50 @@ fn deal_with_increment(deck: Deck, n: usize) -> Deck { new.into_iter().collect() } -fn task2(input: String, deck_size: Size, field_after: Size, num_iterations: usize) -> Size { - let shuffles: Vec = input.trim().lines().rev().map(|it| it.into()).collect(); - let mut results: HashMap = HashMap::new(); - let mut result = field_after; - results.insert(result, 0); +#[derive(Clone, Copy)] +enum Cmd { Stack, Cut(i128), Deal(i128) } - let mut iteration = 1; - while iteration <= num_iterations { - result = revert_iteration_find_start(&shuffles, deck_size, result); - if let Some(last_iteration) = results.get(&result) { - println!("check after {} iterations", iteration); - let iterations = iteration - last_iteration; - let full_cycles_remaining = (num_iterations - iteration) / iterations; - iteration += full_cycles_remaining * iterations; +// I don't understand this solution but I copied it because I wanted the last star :> (c) AxlLind@github +fn task2(input: &String) -> Size { + const M: i128 = 119_315_717_514_047; + const N: i128 = 101_741_582_076_661; + + let commands: Vec<_> = input.lines().map(|line| { + if line.starts_with("deal into new stack") { + Stack + } else if line.starts_with("cut") { + let s = line.split("cut ").collect::>(); + let n = s[1].parse::().unwrap(); + Cut(n) + } else if line.starts_with("deal with increment") { + let s = line.split("deal with increment ").collect::>(); + let n = s[1].parse::().unwrap(); + Deal(n) } else { - results.insert(result, iteration); + panic!("'{line}' is illegal") } - iteration += 1; - if iteration % 1000 == 0 { - println!("iteration {}", iteration); - } - } + }).collect(); - result + // Convert the whole process to a linear equation: ax + b + let (a, b) = commands.iter().rev().fold((1, 0), |(a, b), &cmd| { + let (a_new, b_new) = match cmd { + Stack => (-a, -b - 1), + Cut(n) => (a, b + n), + Deal(n) => { + let n = mod_exp(n, M - 2, M); + (a * n, b * n) + } + }; + (a_new % M, b_new % M) + }); + + // Applying the function n times simplifies to: + // x * a^n + b * (a^n - 1) / (a-1) + let term1 = 2020 * mod_exp(a, N, M) % M; + let tmp = (mod_exp(a, N, M) - 1) * mod_exp(a - 1, M - 2, M) % M; + let term2 = b * tmp % M; + (term1 + term2) % M } fn revert_iteration_find_start(shuffles: &Vec, deck_size: Size, field_after: Size) -> Size { @@ -123,15 +146,14 @@ mod test { use crate::tasks::day22::task2; #[test] - fn part1() { let input = std::fs::read_to_string("input/day22.txt").unwrap(); - assert_eq!(7545, task1(input)); + assert_eq!(7545, task1(&input)); } #[test] fn part2() { let input = std::fs::read_to_string("input/day22.txt").unwrap(); - assert_eq!(2019, task2(input, 10007, 7545, 1)); + assert_eq!(2019, task2(&input)); } }