Use PuzzleUtils
This commit is contained in:
		
							
								
								
									
										6
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -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]]
 | 
			
		||||
 
 | 
			
		||||
@@ -17,3 +17,6 @@ members = [
 | 
			
		||||
	"y2023",
 | 
			
		||||
	"y2024",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[workspace.dependencies]
 | 
			
		||||
utils = { git = "https://git.plobos.xyz/projects/PuzzleUtils.git" }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,3 +5,4 @@ edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
md5 = "0.7.0"
 | 
			
		||||
utils = { workspace = true }
 | 
			
		||||
 
 | 
			
		||||
@@ -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<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 {
 | 
			
		||||
    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 = {
 | 
			
		||||
 
 | 
			
		||||
@@ -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::<Vec<u32>>();
 | 
			
		||||
    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::<u32>() == litres {
 | 
			
		||||
                correct_combinations.push(res);
 | 
			
		||||
            }
 | 
			
		||||
@@ -25,9 +27,10 @@ pub fn process_part2(input: &str, litres: u32) -> u32 {
 | 
			
		||||
        .collect::<Vec<u32>>();
 | 
			
		||||
    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::<u32>() == 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<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)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 
 | 
			
		||||
@@ -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::<usize>() * 10 >= input as usize {
 | 
			
		||||
        if divisors.iter().sum::<u64>() * 10 >= input.into() {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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 {
 | 
			
		||||
    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::<usize>();
 | 
			
		||||
        if sum * 11 >= input as usize {
 | 
			
		||||
        let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::<u64>();
 | 
			
		||||
        if sum * 11 >= input.into() {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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::<Vec<String>>();
 | 
			
		||||
    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<String>, distances: &HashMap<(String, String), u3
 | 
			
		||||
        .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 {
 | 
			
		||||
    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::<Vec<String>>();
 | 
			
		||||
    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;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user