day7: parallel solution, < 100ms
This is definitely a problem that calls for DFS, but I couldn't be bothered.
This commit is contained in:
		
							
								
								
									
										69
									
								
								7/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										69
									
								
								7/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -2,11 +2,44 @@
 | 
			
		||||
# It is not intended for manual editing.
 | 
			
		||||
version = 3
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crossbeam-deque"
 | 
			
		||||
version = "0.8.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crossbeam-epoch",
 | 
			
		||||
 "crossbeam-utils",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crossbeam-epoch"
 | 
			
		||||
version = "0.9.18"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crossbeam-utils",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crossbeam-utils"
 | 
			
		||||
version = "0.8.20"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "day7"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "itertools",
 | 
			
		||||
 "rayon",
 | 
			
		||||
 "thread_local",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -23,3 +56,39 @@ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "either",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "once_cell"
 | 
			
		||||
version = "1.20.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rayon"
 | 
			
		||||
version = "1.10.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "either",
 | 
			
		||||
 "rayon-core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rayon-core"
 | 
			
		||||
version = "1.12.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crossbeam-deque",
 | 
			
		||||
 "crossbeam-utils",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thread_local"
 | 
			
		||||
version = "1.1.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,3 +5,5 @@ edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
itertools = "0.13.0"
 | 
			
		||||
rayon = "1.10.0"
 | 
			
		||||
thread_local = "1.1.8"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										103
									
								
								7/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								7/src/main.rs
									
									
									
									
									
								
							@@ -3,6 +3,8 @@ use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
use std::time::{Duration, Instant};
 | 
			
		||||
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
 | 
			
		||||
use thread_local::ThreadLocal;
 | 
			
		||||
 | 
			
		||||
// BOILERPLATE
 | 
			
		||||
type InputIter = Lines<BufReader<File>>;
 | 
			
		||||
@@ -36,7 +38,7 @@ fn main() {
 | 
			
		||||
    println!("Total duration: {}", duration_format(duration1 + duration2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
struct Calibration {
 | 
			
		||||
    result: u64,
 | 
			
		||||
    numbers: Vec<u64>,
 | 
			
		||||
@@ -55,9 +57,10 @@ impl From<&str> for Calibration {
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct Calibrations {
 | 
			
		||||
    cals: Vec<Calibration>,
 | 
			
		||||
    longest_cal: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
enum Operator {
 | 
			
		||||
    Add,
 | 
			
		||||
    Multiply,
 | 
			
		||||
@@ -69,7 +72,7 @@ impl Operator {
 | 
			
		||||
        match self {
 | 
			
		||||
            Operator::Add => a + b,
 | 
			
		||||
            Operator::Multiply => a * b,
 | 
			
		||||
            Operator::Concatenate => u64::pow(10, b.to_string().len() as u32) * a + b,
 | 
			
		||||
            Operator::Concatenate => u64::pow(10, u64::ilog10(b) + 1) * a + b,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -77,10 +80,55 @@ impl Operator {
 | 
			
		||||
impl<T: BufRead> From<Lines<T>> for Calibrations {
 | 
			
		||||
    fn from(input: Lines<T>) -> Self {
 | 
			
		||||
        let mut cals = Vec::new();
 | 
			
		||||
        let mut longest_cal = 0;
 | 
			
		||||
        for line in input.map(|l| l.unwrap()) {
 | 
			
		||||
            cals.push(line.as_str().into());
 | 
			
		||||
            let cal: Calibration = line.as_str().into();
 | 
			
		||||
            longest_cal = std::cmp::max(longest_cal, cal.numbers.len());
 | 
			
		||||
            cals.push(cal);
 | 
			
		||||
        }
 | 
			
		||||
        Self { cals }
 | 
			
		||||
        Self { cals, longest_cal }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Calibrations {
 | 
			
		||||
    fn make_operator_sets(operators: &[Operator], n_opers: usize) -> Vec<Vec<Vec<Operator>>> {
 | 
			
		||||
        (0..n_opers)
 | 
			
		||||
            .map(|k| {
 | 
			
		||||
                std::iter::repeat_n(operators.iter().map(|v| *v), k)
 | 
			
		||||
                    .multi_cartesian_product()
 | 
			
		||||
                    .collect()
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
    fn check_oper_set(cal: &Calibration, oper_set: &Vec<Operator>) -> bool {
 | 
			
		||||
        let accum = oper_set
 | 
			
		||||
            .iter()
 | 
			
		||||
            .zip(cal.numbers.iter().skip(1))
 | 
			
		||||
            .fold(cal.numbers[0], |accum, (oper, val)| oper.exec(accum, *val));
 | 
			
		||||
        if accum == cal.result {
 | 
			
		||||
            true
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn possible(&self, operators: &[Operator]) -> u64 {
 | 
			
		||||
        let operator_sets = Calibrations::make_operator_sets(operators, self.longest_cal);
 | 
			
		||||
        self.cals
 | 
			
		||||
            .par_iter()
 | 
			
		||||
            .map(|cal| {
 | 
			
		||||
                let n_opers = cal.numbers.len() - 1;
 | 
			
		||||
                let tl = ThreadLocal::new();
 | 
			
		||||
                if operator_sets[n_opers]
 | 
			
		||||
                    .par_iter()
 | 
			
		||||
                    .find_any(|oper_set| Self::check_oper_set(&cal, &oper_set))
 | 
			
		||||
                    .is_some()
 | 
			
		||||
                {
 | 
			
		||||
                    let cal_local = tl.get_or(|| cal.clone());
 | 
			
		||||
                    return cal_local.result;
 | 
			
		||||
                }
 | 
			
		||||
                0
 | 
			
		||||
            })
 | 
			
		||||
            .sum()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -88,56 +136,15 @@ impl<T: BufRead> From<Lines<T>> for Calibrations {
 | 
			
		||||
 | 
			
		||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let cals = Calibrations::from(input);
 | 
			
		||||
    // println!("{:?}", cals);
 | 
			
		||||
    let mut sum = 0;
 | 
			
		||||
 | 
			
		||||
    let operators = [Operator::Add, Operator::Multiply];
 | 
			
		||||
 | 
			
		||||
    for cal in &cals.cals {
 | 
			
		||||
        let n_opers = cal.numbers.len() - 1;
 | 
			
		||||
        // println!("CAL: {:?} (opers: {})", cal, n_opers);
 | 
			
		||||
        for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() {
 | 
			
		||||
            // println!("operator set: {:?}", oper_set);
 | 
			
		||||
            let mut accum = cal.numbers[0];
 | 
			
		||||
            for (i, oper) in oper_set.iter().enumerate() {
 | 
			
		||||
                // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]);
 | 
			
		||||
                accum = oper.exec(accum, cal.numbers[i + 1]);
 | 
			
		||||
            }
 | 
			
		||||
            if accum == cal.result {
 | 
			
		||||
                sum += cal.result;
 | 
			
		||||
                // println!("Matched!");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // println!("NO MATCHES");
 | 
			
		||||
    }
 | 
			
		||||
    sum
 | 
			
		||||
    cals.possible(&operators)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 2 solution
 | 
			
		||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let cals = Calibrations::from(input);
 | 
			
		||||
    let mut sum = 0;
 | 
			
		||||
    let operators = [Operator::Add, Operator::Multiply, Operator::Concatenate];
 | 
			
		||||
    for cal in &cals.cals {
 | 
			
		||||
        let n_opers = cal.numbers.len() - 1;
 | 
			
		||||
        // println!("CAL: {:?} (opers: {})", cal, n_opers);
 | 
			
		||||
        for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() {
 | 
			
		||||
            // println!("operator set: {:?}", oper_set);
 | 
			
		||||
            let mut accum = cal.numbers[0];
 | 
			
		||||
            for (i, oper) in oper_set.iter().enumerate() {
 | 
			
		||||
                // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]);
 | 
			
		||||
                accum = oper.exec(accum, cal.numbers[i + 1]);
 | 
			
		||||
            }
 | 
			
		||||
            if accum == cal.result {
 | 
			
		||||
                sum += cal.result;
 | 
			
		||||
                // println!("Matched!");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // println!("NO MATCHES");
 | 
			
		||||
    }
 | 
			
		||||
    sum
 | 
			
		||||
    cals.possible(&operators)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user