diff --git a/Cargo.lock b/Cargo.lock index 62ef042..d7c455f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,11 +12,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +[[package]] +name = "utils" +version = "0.1.0" +source = "git+https://git.plobos.xyz/projects/PuzzleUtils.git#49b0f24c1bdc2c04df237634607df15f19fb3ead" + [[package]] name = "y2015" version = "0.1.0" dependencies = [ "md5", + "utils", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1bf1cb9..ab40e13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ members = [ "y2023", "y2024", ] + +[workspace.dependencies] +utils = { git = "https://git.plobos.xyz/projects/PuzzleUtils.git" } diff --git a/y2015/Cargo.toml b/y2015/Cargo.toml index c30cc93..303c651 100644 --- a/y2015/Cargo.toml +++ b/y2015/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] md5 = "0.7.0" +utils = { workspace = true } diff --git a/y2015/src/days/d13.rs b/y2015/src/days/d13.rs index 96d45f8..90a14e1 100644 --- a/y2015/src/days/d13.rs +++ b/y2015/src/days/d13.rs @@ -1,5 +1,7 @@ use core::panic; -use std::{collections::HashMap, error::Error}; +use std::collections::HashMap; + +use utils::{math::factorial, permutation}; pub fn process_part1(input: &str) -> i32 { let mut happiness_table = HashMap::new(); @@ -22,12 +24,12 @@ pub fn process_part1(input: &str) -> i32 { let num_people = people.len(); - let possible_permutations = factorial(num_people); + let possible_permutations = factorial(num_people.try_into().unwrap()); let mut highest_happiness = 0; for idx in 1..possible_permutations { let mut happiness = 0; - let permutation = permutation(people.clone(), idx).unwrap(); + let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap(); for (pos, person) in permutation.clone().iter().enumerate() { let left_neighbor = { @@ -62,34 +64,6 @@ pub fn process_part1(input: &str) -> i32 { highest_happiness } -fn permutation(mut people: Vec, nth: usize) -> Result, Box> { - people.sort(); - if nth == 1 { - return Ok(people); - } - if nth > factorial(people.len()) || nth == 0 { - return Err(Box::from("Out of bounds")); - } - let mut perm = Vec::new(); - let num_unique_people = people.len(); - let mut remainder = nth - 1; - for idx in 1..=people.len() { - let permutations = remainder / factorial(num_unique_people - idx); - remainder %= factorial(num_unique_people - idx); - perm.push(people[permutations].clone()); - people.remove(permutations); - } - Ok(perm) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - pub fn process_part2(input: &str) -> i32 { let mut happiness_table = HashMap::new(); let mut people = Vec::new(); @@ -118,12 +92,12 @@ pub fn process_part2(input: &str) -> i32 { let num_people = people.len(); - let possible_permutations = factorial(num_people); + let possible_permutations = factorial(num_people.try_into().unwrap()); let mut highest_happiness = 0; for idx in 1..possible_permutations { let mut happiness = 0; - let permutation = permutation(people.clone(), idx).unwrap(); + let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap(); for (pos, person) in permutation.clone().iter().enumerate() { let left_neighbor = { diff --git a/y2015/src/days/d17.rs b/y2015/src/days/d17.rs index fa608b9..d5492c7 100644 --- a/y2015/src/days/d17.rs +++ b/y2015/src/days/d17.rs @@ -1,4 +1,5 @@ -use std::{error::Error, fmt::Debug}; +use utils::combination; +use utils::math::binomial; pub fn process_part1(input: &str, litres: u32) -> u32 { let containers = input @@ -7,9 +8,10 @@ pub fn process_part1(input: &str, litres: u32) -> u32 { .collect::>(); let mut correct_combinations = Vec::new(); for k in 1..=containers.len() { - let num_combinations = binomial(containers.len(), k); + let num_combinations = + binomial(containers.len().try_into().unwrap(), k.try_into().unwrap()); for i in 1..=num_combinations { - let res = combination(containers.clone(), k, i).unwrap(); + let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap(); if res.iter().sum::() == litres { correct_combinations.push(res); } @@ -25,9 +27,10 @@ pub fn process_part2(input: &str, litres: u32) -> u32 { .collect::>(); let mut correct_combinations = Vec::new(); for k in 1..=containers.len() { - let num_combinations = binomial(containers.len(), k); + let num_combinations = + binomial(containers.len().try_into().unwrap(), k.try_into().unwrap()); for i in 1..=num_combinations { - let res = combination(containers.clone(), k, i).unwrap(); + let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap(); if res.iter().sum::() == litres { correct_combinations.push(res); } @@ -39,56 +42,6 @@ pub fn process_part2(input: &str, litres: u32) -> u32 { correct_combinations.len() as u32 } -fn combination( - elements: Vec, - k: usize, - nth: usize, -) -> Result, Box> { - let num_elements = elements.len(); - let num_combinations = binomial(num_elements, k); - if nth > num_combinations || k > num_elements || nth == 0 || k == 0 { - return Err(Box::from("Out of bounds")); - } - let mut i = 0; - let mut remaining_k = k; - let mut comb = Vec::new(); - let mut remainder = nth - 1; - while remaining_k > 0 { - // Count the number of combinations that start with elements[i] - // example with n = 5, k = 2 - // nth <= 4 select first - // nth <= 7 select second - // nth <= 9 select third - // nth == 10 select fourth - let count = binomial(num_elements - i - 1, remaining_k - 1); - if remainder < count { - // If the nth combination is within the count, pick this element - comb.push(elements[i].clone()); - remaining_k -= 1; - } else { - remainder -= count; - } - i += 1; - } - Ok(comb) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - -fn binomial(n: usize, k: usize) -> usize { - if k > n { - 0 - } else { - factorial(n) / (factorial(k) * factorial(n - k)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/y2015/src/days/d20.rs b/y2015/src/days/d20.rs index 9b8d2bb..e321899 100644 --- a/y2015/src/days/d20.rs +++ b/y2015/src/days/d20.rs @@ -1,3 +1,5 @@ +use utils::math::get_divisors; + pub fn process_part1(input: u32) -> u32 { // slow //let mut house = 1; @@ -20,38 +22,21 @@ pub fn process_part1(input: u32) -> u32 { house += 1; let mut divisors = get_divisors(house); divisors.push(house); - if divisors.iter().sum::() * 10 >= input as usize { + if divisors.iter().sum::() * 10 >= input.into() { break; } } house as u32 } -fn get_divisors(n: usize) -> Vec { - let mut divisors = vec![1]; - let mut potential_divisor = 2; - while (potential_divisor * potential_divisor) < n { - if n % potential_divisor == 0 { - divisors.push(potential_divisor); - divisors.push(n / potential_divisor); - } - potential_divisor += 1; - } - // This almost made me go mad - if potential_divisor * potential_divisor == n { - divisors.push(potential_divisor) - } - divisors -} - pub fn process_part2(input: u32) -> u32 { let mut house = 0; loop { house += 1; let mut divisors = get_divisors(house); divisors.push(house); - let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::(); - if sum * 11 >= input as usize { + let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::(); + if sum * 11 >= input.into() { break; } } diff --git a/y2015/src/days/d9.rs b/y2015/src/days/d9.rs index c3319f4..ff8e0f5 100644 --- a/y2015/src/days/d9.rs +++ b/y2015/src/days/d9.rs @@ -1,8 +1,7 @@ use core::panic; -use std::{ - collections::{HashMap, HashSet}, - error::Error, -}; +use std::collections::{HashMap, HashSet}; + +use utils::{math::factorial, permutation::nth_lex}; pub fn process_part1(input: &str) -> u32 { let mut distances = HashMap::new(); @@ -19,10 +18,10 @@ pub fn process_part1(input: &str) -> u32 { .iter() .map(|place| place.to_string()) .collect::>(); - let num_permutations = factorial(locations.len()); + let num_permutations = factorial(locations.len().try_into().unwrap()); let mut shortest = u32::MAX; for idx in 1..=num_permutations { - let perm = permutation(locations.clone(), idx).unwrap(); + let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap(); let route_length = get_route_length(perm, &distances); if route_length < shortest { shortest = route_length; @@ -48,34 +47,6 @@ fn get_route_length(route: Vec, distances: &HashMap<(String, String), u3 .sum() } -fn permutation(mut locations: Vec, nth: usize) -> Result, Box> { - locations.sort(); - if nth == 1 { - return Ok(locations); - } - if nth > factorial(locations.len()) || nth == 0 { - return Err(Box::from("Out of bounds")); - } - let mut perm = Vec::new(); - let num_unique_locations = locations.len(); - let mut remainder = nth - 1; - for idx in 1..=locations.len() { - let permutations = remainder / factorial(num_unique_locations - idx); - remainder %= factorial(num_unique_locations - idx); - perm.push(locations[permutations].clone()); - locations.remove(permutations); - } - Ok(perm) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - pub fn process_part2(input: &str) -> u32 { let mut distances = HashMap::new(); let mut locations = HashSet::new(); @@ -91,10 +62,10 @@ pub fn process_part2(input: &str) -> u32 { .iter() .map(|place| place.to_string()) .collect::>(); - let num_permutations = factorial(locations.len()); + let num_permutations = factorial(locations.len().try_into().unwrap()); let mut longest = 0; for idx in 1..=num_permutations { - let perm = permutation(locations.clone(), idx).unwrap(); + let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap(); let route_length = get_route_length(perm, &distances); if route_length > longest { longest = route_length;