day11: performance

This commit is contained in:
Keenan Tims 2024-12-11 00:55:47 -08:00
parent a7354b6ed7
commit 2e239681ce
Signed by: ktims
GPG Key ID: 11230674D69038D4
3 changed files with 90 additions and 22 deletions

46
11/Cargo.lock generated
View File

@ -2,11 +2,37 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[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]] [[package]]
name = "day11" name = "day11"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"itertools", "itertools",
"rayon",
] ]
[[package]] [[package]]
@ -23,3 +49,23 @@ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [ dependencies = [
"either", "either",
] ]
[[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",
]

View File

@ -5,3 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
itertools = "0.13.0" itertools = "0.13.0"
rayon = "1.10.0"

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Lines}; use std::io::{BufRead, BufReader, Lines};
use std::iter::repeat;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use itertools::Itertools; use itertools::Itertools;
@ -42,46 +43,67 @@ struct Stone {
value: u64, value: u64,
} }
enum BlinkResult {
One(Stone),
Two(Stone, Stone),
}
impl Stone { impl Stone {
fn blink_once(self) -> Vec<Stone> { fn blink_once(self) -> BlinkResult {
let n_digits = if self.value == 0 { 1 } else { self.value.ilog10() + 1 }; let n_digits = if self.value == 0 { 1 } else { self.value.ilog10() + 1 };
if self.value == 0 { if self.value == 0 {
vec![Stone { value: 1 }] BlinkResult::One(Stone { value: 1 })
} else if n_digits % 2 == 0 { } else if n_digits % 2 == 0 {
let parts = ( let parts = (
self.value / 10u64.pow(n_digits / 2), self.value / 10u64.pow(n_digits / 2),
self.value % 10u64.pow(n_digits / 2), self.value % 10u64.pow(n_digits / 2),
); );
vec![Stone { value: parts.0 }, Stone { value: parts.1 }] BlinkResult::Two(Stone { value: parts.0 }, Stone { value: parts.1 })
} else { } else {
vec![Stone { BlinkResult::One(Stone {
value: self.value * 2024, value: self.value * 2024,
}] })
} }
} }
fn blink(self, times: usize) -> Vec<Stone> { // #[allow(dead_code)]
let mut stones = vec![self]; // fn blink(self, times: usize) -> Vec<Stone> {
for _ in 0..times { // // Used in submitted part 1 solution
stones = stones.iter().flat_map(|stone| stone.blink_once()).collect(); // let mut stones = vec![self];
} // for _ in 0..times {
stones // stones = stones.iter().flat_map(|stone| stone.blink_once()).collect();
} // }
// stones
// }
} }
fn count_blinks(stone: Stone, blinks: u64, cache: &mut HashMap<(u64, Stone), u64>) -> u64 { fn count_blinks(stone: Stone, blink: usize, cache: &mut Vec<HashMap<Stone, u64>>) -> u64 {
if cache.contains_key(&(blinks, stone)) { if cache[blink].contains_key(&stone) {
return cache[&(blinks, stone)]; return cache[blink][&stone];
} }
let stones = stone.blink_once(); let stones = stone.blink_once();
let result = if blinks == 1 { let result = if blink == 0 {
stones.len() as u64 match stones {
BlinkResult::One(_) => 1,
BlinkResult::Two(_, _) => 2,
}
} else { } else {
stones.iter().map(|s| count_blinks(*s, blinks - 1, cache)).sum() match stones {
BlinkResult::One(s) => count_blinks(s, blink - 1, cache),
BlinkResult::Two(s1, s2) => count_blinks(s1, blink - 1, cache) + count_blinks(s2, blink - 1, cache),
}
}; };
cache.insert((blinks, stone), result); cache[blink].insert(stone, result);
result result
} }
fn blink_stones(stones: &[Stone], blinks: usize) -> u64 {
let mut cache = Vec::from_iter(repeat(HashMap::new()).take(blinks));
stones
.iter()
.map(|stone| count_blinks(*stone, blinks - 1, &mut cache))
.sum()
}
// PROBLEM 1 solution // PROBLEM 1 solution
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 { fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
@ -94,7 +116,7 @@ fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
value: v.parse().unwrap(), value: v.parse().unwrap(),
}) })
.collect_vec(); .collect_vec();
stones.iter().flat_map(|stone| stone.blink(25)).count() as u64 blink_stones(&stones, 25)
} }
// PROBLEM 2 solution // PROBLEM 2 solution
@ -108,8 +130,7 @@ fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
value: v.parse().unwrap(), value: v.parse().unwrap(),
}) })
.collect_vec(); .collect_vec();
let mut cache = HashMap::new(); blink_stones(&stones, 75)
stones.iter().map(|s| count_blinks(*s, 75, &mut cache)).sum()
} }
#[cfg(test)] #[cfg(test)]