day22: perf and clippies, down to < 1s
Some checks failed
test / AoC 2024 (push) Failing after 4m2s

This commit is contained in:
Keenan Tims 2024-12-22 02:21:13 -08:00
parent c5857ed449
commit d31f9725f5
Signed by: ktims
GPG Key ID: 11230674D69038D4

View File

@ -1,28 +1,31 @@
use aoc_runner_derive::aoc; use aoc_runner_derive::aoc;
use itertools::Itertools; use itertools::Itertools;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use rustc_hash::FxHashMap;
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
struct Change { struct Change {
price: i8, price: i8,
delta: i8, delta: i8,
} }
fn evolve_secret(mut n: i64) -> i64 { type Secret = u64;
fn evolve_secret(mut n: Secret) -> Secret {
n = ((n * 64) ^ n) % 16777216; n = ((n * 64) ^ n) % 16777216;
n = ((n / 32) ^ n) % 16777216; n = ((n / 32) ^ n) % 16777216;
n = ((n * 2048) ^ n) % 16777216; n = ((n * 2048) ^ n) % 16777216;
n n
} }
fn rounds(mut secret: i64, n: i64) -> i64 { fn rounds(mut secret: Secret, n: Secret) -> Secret {
for _ in 0..n { for _ in 0..n {
secret = evolve_secret(secret) secret = evolve_secret(secret)
} }
secret secret
} }
fn prices(mut secret: i64, n: i64) -> Vec<i8> { fn prices(mut secret: Secret, n: usize) -> Vec<i8> {
let mut prices = vec![(secret % 10) as i8]; let mut prices = vec![(secret % 10) as i8];
for _ in 1..n { for _ in 1..n {
secret = evolve_secret(secret); secret = evolve_secret(secret);
@ -31,60 +34,66 @@ fn prices(mut secret: i64, n: i64) -> Vec<i8> {
prices prices
} }
fn changes(prices: &[i8]) -> Vec<Change> { fn build_profit_map(prices: &[i8]) -> FxHashMap<[i8; 4], i8> {
prices let mut profits = FxHashMap::default();
let changes = prices
.windows(2) .windows(2)
.map(|a| Change { .map(|a| Change {
price: a[1], price: a[1],
delta: a[1] - a[0], delta: a[1] - a[0],
}) })
.collect() .collect_vec();
for i in 3..changes.len() {
let seq: [i8; 4] = changes[i - 3..=i]
.iter()
.map(|c| c.delta)
.collect_vec()
.try_into()
.unwrap();
profits.entry(seq).or_insert(changes[i].price);
}
profits
} }
fn profit_for_sequence(changes: &Vec<Vec<Change>>, seq: &[i8]) -> i64 { fn profit_for_sequence(changes: &[FxHashMap<[i8; 4], i8>], seq: &[i8]) -> i64 {
changes changes
.par_iter() .iter()
.filter_map(|inner| { .filter_map(|inner| inner.get(seq).map(|v| *v as i64))
inner
.windows(seq.len())
.find(|window| window.iter().zip(seq).all(|(w, z)| w.delta == *z))
.map(|buy| buy[seq.len() - 1].price as i64)
})
.sum() .sum()
} }
fn find_best_sequence(changes: &Vec<Vec<Change>>) -> [i8; 4] { fn find_best_sequence(changes: &[FxHashMap<[i8; 4], i8>]) -> [i8; 4] {
let mut best_seq = [0, 0, 0, 0]; let possible_seqs = (0..4).map(|_| (-9..=9i8)).multi_cartesian_product().collect_vec();
let mut best_profit = 0; let (best_seq, _best_profit) = possible_seqs
for seq in (0..4).map(|_| (-9..=9i8)).multi_cartesian_product() { .par_iter()
let profit = profit_for_sequence(changes, &seq); .map_with(changes, |changes, seq| (seq, profit_for_sequence(changes, seq)))
if profit > best_profit { .max_by(|(_, a), (_, b)| a.cmp(b))
best_seq = seq.try_into().unwrap(); .unwrap();
best_profit = profit;
} best_seq.as_slice().try_into().unwrap()
}
best_seq
} }
fn parse(input: &str) -> Vec<i64> { fn parse(input: &str) -> Vec<Secret> {
input.lines().map(|l| l.parse().unwrap()).collect() input.lines().map(|l| l.parse().unwrap()).collect()
} }
#[aoc(day22, part1)] #[aoc(day22, part1)]
pub fn part1(input: &str) -> i64 { pub fn part1(input: &str) -> Secret {
let secrets = parse(input); let secrets = parse(input);
secrets.iter().map(|s| rounds(*s, 2000)).sum::<i64>() secrets.iter().map(|s| rounds(*s, 2000)).sum::<Secret>()
} }
#[aoc(day22, part2)] #[aoc(day22, part2)]
pub fn part2(input: &str) -> i64 { pub fn part2(input: &str) -> i64 {
let secrets = parse(input); let secrets = parse(input);
let price_changes = secrets.iter().map(|s| changes(&prices(*s, 2000))).collect_vec(); let price_changes = secrets
.iter()
.map(|s| build_profit_map(&prices(*s, 2000)))
.collect_vec();
let seq = find_best_sequence(&price_changes); let seq = find_best_sequence(&price_changes);
println!("found best seq: {:?}", seq);
profit_for_sequence(&price_changes, &seq) profit_for_sequence(&price_changes, &seq)
} }
@ -122,28 +131,18 @@ mod tests {
#[test] #[test]
fn test_profit() { fn test_profit() {
assert_eq!( assert_eq!(
profit_for_sequence(&vec![changes(&prices(123, 10))], &[-1, -1, 0, 2]), profit_for_sequence(&vec![build_profit_map(&prices(123, 10))], &[-1, -1, 0, 2]),
6 6
); );
let secrets = parse(EXAMPLE2); let secrets = parse(EXAMPLE2);
let price_changes = secrets.iter().map(|s| changes(&prices(*s, 2000))).collect_vec(); let price_changes = secrets
.iter()
.map(|s| build_profit_map(&prices(*s, 2000)))
.collect_vec();
assert_eq!(profit_for_sequence(&price_changes, &[-2, 1, -1, 3]), 23); assert_eq!(profit_for_sequence(&price_changes, &[-2, 1, -1, 3]), 23);
} }
#[test]
fn test_changes() {
let changes = changes(&prices(123, 10));
assert_eq!(
changes.iter().map(|c| c.delta).collect_vec(),
vec![-3, 6, -1, -1, 0, 2, -2, 0, -2]
);
assert_eq!(
changes.iter().map(|c| c.price).collect_vec(),
vec![0, 6, 5, 4, 4, 6, 4, 4, 2]
);
}
#[test] #[test]
fn part1_example() { fn part1_example() {
assert_eq!(part1(EXAMPLE), 37327623); assert_eq!(part1(EXAMPLE), 37327623);