Compare commits

..

12 Commits

Author SHA1 Message Date
c665ed878e Use Arc to not clone cached permutations 2025-06-30 10:57:30 -05:00
8825d97858 Improve type names 2025-06-30 10:06:29 -05:00
4250028c6a Remove unused import 2025-06-16 13:51:58 -05:00
16e726db6b Make const "sets" 2025-06-16 13:51:07 -05:00
ec0267a455 Only make a single cache key (minor optimization) 2025-06-16 13:36:02 -05:00
a7b3d9225b Remove commented out code 2025-06-16 13:33:05 -05:00
f235c46bd4 Add caching back in 2025-06-16 13:32:26 -05:00
3815d496b9 Finish day 7 part 2 2025-06-16 13:07:41 -05:00
9f25697af4 Comment out old implementation 2025-06-16 12:59:46 -05:00
e2cb839086 Better permutation creation 2025-06-16 12:58:38 -05:00
72492db6ea Move over to using enum 2025-06-16 12:24:18 -05:00
3dff9f9bd2 Optimize set creation 2025-06-16 12:17:24 -05:00
3 changed files with 106 additions and 54 deletions

View File

@@ -1,12 +1,8 @@
mod operator_set;
use operator_set::create_operator_set;
use std::ops::Deref;
#[derive(Debug, Clone, Copy)]
enum Ops {
Multiply,
Add,
}
use operator_set::{Op, create_permutations};
#[derive(Debug)]
pub struct Equation {
@@ -52,31 +48,46 @@ impl Equation {
self.answer
}
pub fn components(&self) -> &Vec<i64> {
&self.components
}
fn compute_answer(&self, operators: &[bool]) -> i64 {
fn compute_answer(&self, operators: &[Op]) -> i64 {
let mut accumulator = self.components[0];
for (i, op_bool) in operators.iter().enumerate() {
if *op_bool {
// Multiply
accumulator *= self.components[i + 1];
} else {
// Add
accumulator += self.components[i + 1];
for (i, op) in operators.iter().enumerate() {
match op {
Op::Multiply => accumulator *= self.components[i + 1],
Op::Add => accumulator += self.components[i + 1],
Op::Concat => {
let accumulator_string = format!("{}", accumulator);
let component_string = format!("{}", self.components[i + 1]);
accumulator = (accumulator_string + &component_string).parse().unwrap();
}
}
}
accumulator
}
pub fn solvable(&self) -> bool {
let op_combinations = create_operator_set(self.components.len() - 1);
const MULTIPLY_ADD_SET: [Op; 2] = [Op::Add, Op::Multiply];
pub fn solvable_multiply_add(&self) -> bool {
let op_combinations =
create_permutations(&Equation::MULTIPLY_ADD_SET, self.components.len() - 1);
for combination in op_combinations {
if self.answer == self.compute_answer(&combination) {
for combination in op_combinations.deref() {
if self.answer == self.compute_answer(combination) {
return true;
}
}
false
}
const MULTIPLY_ADD_CONCAT_SET: [Op; 3] = [Op::Add, Op::Multiply, Op::Concat];
pub fn solvable_multiply_add_concat(&self) -> bool {
let op_combinations = create_permutations(
&Equation::MULTIPLY_ADD_CONCAT_SET,
self.components.len() - 1,
);
for combination in op_combinations.deref() {
if self.answer == self.compute_answer(combination) {
return true;
}
}

View File

@@ -1,53 +1,79 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::{LazyLock, Mutex};
use std::sync::{Arc, LazyLock, Mutex};
type OperatorCache = HashMap<usize, HashSet<Vec<bool>>>;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Op {
Multiply,
Add,
Concat,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct PermutationCacheKey {
size: usize,
set: Vec<Op>,
}
impl PermutationCacheKey {
fn new(size: usize, set: Vec<Op>) -> PermutationCacheKey {
PermutationCacheKey { size, set }
}
}
type Permutation = Vec<Op>;
type OperatorCache = HashMap<PermutationCacheKey, Arc<HashSet<Permutation>>>;
static OPERATOR_SET_CACHE: LazyLock<Mutex<OperatorCache>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
fn check_operator_set_cache(cache_key: usize) -> Option<HashSet<Vec<bool>>> {
OPERATOR_SET_CACHE.lock().unwrap().get(&cache_key).cloned()
fn check_operator_set_cache(cache_key: &PermutationCacheKey) -> Option<Arc<HashSet<Permutation>>> {
let lock = OPERATOR_SET_CACHE.lock().unwrap();
let cache_value = lock.get(cache_key);
cache_value.cloned()
}
fn store_operator_set_in_cache(cache_key: usize, cache_value: HashSet<Vec<bool>>) {
fn store_operator_set_in_cache(
cache_key: PermutationCacheKey,
cache_value: Arc<HashSet<Permutation>>,
) {
OPERATOR_SET_CACHE
.lock()
.unwrap()
.insert(cache_key, cache_value);
}
fn create_operator_set_recurse(
operators_permutation_set: &mut HashSet<Vec<bool>>,
operators: Vec<bool>,
fn create_permutations_recurse(
values: &[Op],
len: usize,
permutations: &mut HashSet<Permutation>,
curr_permutation: Permutation,
) {
operators_permutation_set.insert(operators.clone());
if !operators.contains(&false) {
if curr_permutation.len() == len {
permutations.insert(curr_permutation);
return;
}
for i in 0..operators.len() {
if operators[i] {
continue;
}
let mut new_operators = operators.clone();
new_operators[i] = true;
create_operator_set_recurse(operators_permutation_set, new_operators);
for v in values {
let mut next_permutation = curr_permutation.clone();
next_permutation.push(*v);
create_permutations_recurse(values, len, permutations, next_permutation);
}
}
pub fn create_operator_set(num_of_operators: usize) -> HashSet<Vec<bool>> {
pub fn create_permutations(set: &[Op], len: usize) -> Arc<HashSet<Permutation>> {
// Check cache
if let Some(cached_value) = check_operator_set_cache(num_of_operators) {
return cached_value.clone();
let cache_key = PermutationCacheKey::new(len, set.to_vec());
if let Some(cached_value) = check_operator_set_cache(&cache_key) {
return cached_value;
}
let mut operator_sets: HashSet<Vec<bool>> = HashSet::new();
create_operator_set_recurse(&mut operator_sets, vec![false; num_of_operators]);
// Create permutations
let mut permutations = HashSet::new();
create_permutations_recurse(set, len, &mut permutations, vec![]);
// Store value in cache
store_operator_set_in_cache(num_of_operators, operator_sets.clone());
operator_sets
// Cache and return
let permutations = Arc::new(permutations);
store_operator_set_in_cache(cache_key, permutations.clone());
permutations
}

View File

@@ -33,19 +33,34 @@ fn main() -> Result<(), Box<dyn Error>> {
equations.push(Equation::new(&line)?)
}
let mut value_sum = 0;
let mut result_add_multiply = 0;
let mut result_add_multiply_concat = 0;
println!("Is solvable?");
println!("------------");
for equation in equations {
let solvable = equation.solvable();
println!("{:?} => {}", equation, solvable);
if solvable {
value_sum += equation.answer();
let solvable_add_multiply = equation.solvable_multiply_add();
let solvable_add_multiply_concat = equation.solvable_multiply_add_concat();
println!(
"{:?} => {} | {}",
equation, solvable_add_multiply, solvable_add_multiply_concat
);
if solvable_add_multiply {
result_add_multiply += equation.answer();
}
if solvable_add_multiply_concat {
result_add_multiply_concat += equation.answer();
}
}
println!("\n Total Calibration Result: {}", value_sum);
println!(
"\n Total Calibration Result (ADD|MULTIPLY): {}",
result_add_multiply
);
println!(
"\n Total Calibration Result (ADD|MULTIPLY|CONCAT): {}",
result_add_multiply_concat
);
Ok(())
}