Use PuzzleUtils

This commit is contained in:
Fabian Schmidt 2024-11-18 11:52:30 +01:00
parent a875d889fa
commit 3bb1616d7e
7 changed files with 37 additions and 144 deletions

6
Cargo.lock generated
View File

@ -12,11 +12,17 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "utils"
version = "0.1.0"
source = "git+https://git.plobos.xyz/projects/PuzzleUtils.git#49b0f24c1bdc2c04df237634607df15f19fb3ead"
[[package]] [[package]]
name = "y2015" name = "y2015"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"md5", "md5",
"utils",
] ]
[[package]] [[package]]

View File

@ -17,3 +17,6 @@ members = [
"y2023", "y2023",
"y2024", "y2024",
] ]
[workspace.dependencies]
utils = { git = "https://git.plobos.xyz/projects/PuzzleUtils.git" }

View File

@ -5,3 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
md5 = "0.7.0" md5 = "0.7.0"
utils = { workspace = true }

View File

@ -1,5 +1,7 @@
use core::panic; 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 { pub fn process_part1(input: &str) -> i32 {
let mut happiness_table = HashMap::new(); let mut happiness_table = HashMap::new();
@ -22,12 +24,12 @@ pub fn process_part1(input: &str) -> i32 {
let num_people = people.len(); 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; let mut highest_happiness = 0;
for idx in 1..possible_permutations { for idx in 1..possible_permutations {
let mut happiness = 0; 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() { for (pos, person) in permutation.clone().iter().enumerate() {
let left_neighbor = { let left_neighbor = {
@ -62,34 +64,6 @@ pub fn process_part1(input: &str) -> i32 {
highest_happiness highest_happiness
} }
fn permutation(mut people: Vec<String>, nth: usize) -> Result<Vec<String>, Box<dyn Error>> {
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 { pub fn process_part2(input: &str) -> i32 {
let mut happiness_table = HashMap::new(); let mut happiness_table = HashMap::new();
let mut people = Vec::new(); let mut people = Vec::new();
@ -118,12 +92,12 @@ pub fn process_part2(input: &str) -> i32 {
let num_people = people.len(); 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; let mut highest_happiness = 0;
for idx in 1..possible_permutations { for idx in 1..possible_permutations {
let mut happiness = 0; 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() { for (pos, person) in permutation.clone().iter().enumerate() {
let left_neighbor = { let left_neighbor = {

View File

@ -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 { pub fn process_part1(input: &str, litres: u32) -> u32 {
let containers = input let containers = input
@ -7,9 +8,10 @@ pub fn process_part1(input: &str, litres: u32) -> u32 {
.collect::<Vec<u32>>(); .collect::<Vec<u32>>();
let mut correct_combinations = Vec::new(); let mut correct_combinations = Vec::new();
for k in 1..=containers.len() { 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 { 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::<u32>() == litres { if res.iter().sum::<u32>() == litres {
correct_combinations.push(res); correct_combinations.push(res);
} }
@ -25,9 +27,10 @@ pub fn process_part2(input: &str, litres: u32) -> u32 {
.collect::<Vec<u32>>(); .collect::<Vec<u32>>();
let mut correct_combinations = Vec::new(); let mut correct_combinations = Vec::new();
for k in 1..=containers.len() { 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 { 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::<u32>() == litres { if res.iter().sum::<u32>() == litres {
correct_combinations.push(res); correct_combinations.push(res);
} }
@ -39,56 +42,6 @@ pub fn process_part2(input: &str, litres: u32) -> u32 {
correct_combinations.len() as u32 correct_combinations.len() as u32
} }
fn combination<T: Clone + Debug>(
elements: Vec<T>,
k: usize,
nth: usize,
) -> Result<Vec<T>, Box<dyn Error>> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,3 +1,5 @@
use utils::math::get_divisors;
pub fn process_part1(input: u32) -> u32 { pub fn process_part1(input: u32) -> u32 {
// slow // slow
//let mut house = 1; //let mut house = 1;
@ -20,38 +22,21 @@ pub fn process_part1(input: u32) -> u32 {
house += 1; house += 1;
let mut divisors = get_divisors(house); let mut divisors = get_divisors(house);
divisors.push(house); divisors.push(house);
if divisors.iter().sum::<usize>() * 10 >= input as usize { if divisors.iter().sum::<u64>() * 10 >= input.into() {
break; break;
} }
} }
house as u32 house as u32
} }
fn get_divisors(n: usize) -> Vec<usize> {
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 { pub fn process_part2(input: u32) -> u32 {
let mut house = 0; let mut house = 0;
loop { loop {
house += 1; house += 1;
let mut divisors = get_divisors(house); let mut divisors = get_divisors(house);
divisors.push(house); divisors.push(house);
let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::<usize>(); let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::<u64>();
if sum * 11 >= input as usize { if sum * 11 >= input.into() {
break; break;
} }
} }

View File

@ -1,8 +1,7 @@
use core::panic; use core::panic;
use std::{ use std::collections::{HashMap, HashSet};
collections::{HashMap, HashSet},
error::Error, use utils::{math::factorial, permutation::nth_lex};
};
pub fn process_part1(input: &str) -> u32 { pub fn process_part1(input: &str) -> u32 {
let mut distances = HashMap::new(); let mut distances = HashMap::new();
@ -19,10 +18,10 @@ pub fn process_part1(input: &str) -> u32 {
.iter() .iter()
.map(|place| place.to_string()) .map(|place| place.to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let num_permutations = factorial(locations.len()); let num_permutations = factorial(locations.len().try_into().unwrap());
let mut shortest = u32::MAX; let mut shortest = u32::MAX;
for idx in 1..=num_permutations { 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); let route_length = get_route_length(perm, &distances);
if route_length < shortest { if route_length < shortest {
shortest = route_length; shortest = route_length;
@ -48,34 +47,6 @@ fn get_route_length(route: Vec<String>, distances: &HashMap<(String, String), u3
.sum() .sum()
} }
fn permutation(mut locations: Vec<String>, nth: usize) -> Result<Vec<String>, Box<dyn Error>> {
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 { pub fn process_part2(input: &str) -> u32 {
let mut distances = HashMap::new(); let mut distances = HashMap::new();
let mut locations = HashSet::new(); let mut locations = HashSet::new();
@ -91,10 +62,10 @@ pub fn process_part2(input: &str) -> u32 {
.iter() .iter()
.map(|place| place.to_string()) .map(|place| place.to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let num_permutations = factorial(locations.len()); let num_permutations = factorial(locations.len().try_into().unwrap());
let mut longest = 0; let mut longest = 0;
for idx in 1..=num_permutations { 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); let route_length = get_route_length(perm, &distances);
if route_length > longest { if route_length > longest {
longest = route_length; longest = route_length;