part 2 should not have taken this long

This commit is contained in:
Joseph Montanaro 2021-12-04 13:41:12 -08:00
parent b1a8ab8bcd
commit 340ef2d4d3

View File

@ -1,41 +1,71 @@
use color_eyre::eyre;
fn most_common_bits(numbers: &Vec<u16>) -> u16 {
let mut bit_diffs: [isize; 12] = [0; 12];
for num in numbers {
for i in 0..11 {
if num & (1 << i) > 0 {
bit_diffs[i] += 1
}
else {
bit_diffs[i] -= 1
}
}
}
fn most_common_bit(numbers: &Vec<u16>, pos: u8) -> u16 {
// number of times the bit in question is 1,
// minus the number of times it's 0
let mask: u16 = 1 << pos;
let total: isize = numbers
.iter()
.map(|n| if n & mask > 0 {1} else {-1})
.sum();
let mut result = 0u16;
for (i, sum) in bit_diffs.iter().enumerate() {
let bit = if *sum >= 0 {1} else {0};
result |= bit << i;
}
result
}
fn invert_12bit(n: u16) -> u16 {
!n & (65535 >> 4) // since these are really only 12-bit numbers we have to clear the 4 spurious 1's that come from inverting the original
// if the total is positive, then 1 is more frequent than 0
if total >= 0 {1} else {0}
}
fn part1(numbers: &Vec<u16>) -> u64 {
let gamma = most_common_bits(numbers);
let epsilon = invert_12bit(gamma);
let mut gamma = 0u16;
for i in 0..12 {
let mask = most_common_bit(numbers, i) << i;
gamma |= mask;
}
// alternate solution: it feels super cool, but it's not any fewer lines than the above, and way less clear :(
// let gamma = (0..12)
// .fold(
// 0u16,
// |g, i| g | (most_common_bit(numbers, i) << i)
// );
// epsilon is just the bit inverse of gamma, but since these are really 12-bit numbers
// we have to clear the 4 spurious 1's that come from inverting the original
let epsilon = !gamma & (65535 >> 4);
epsilon as u64 * gamma as u64
}
fn filter_bit_criteria(numbers: Vec<u16>, bit_pos: u8, bit_value: u16) -> Vec<u16> {
numbers
.into_iter()
.filter(|n| (n >> bit_pos) & 1 == bit_value)
.collect()
}
fn part2(numbers: &Vec<u16>) -> u64 {
let mut oxy = numbers.clone();
let mut co2 = numbers.clone();
for pos in (0..12).rev() {
if oxy.len() > 1 {
let oxy_bit = most_common_bit(&oxy, pos);
oxy = filter_bit_criteria(oxy, pos, oxy_bit);
}
if co2.len() > 1 {
let co2_bit = most_common_bit(&co2, pos) ^ 1; // flip the rightmost bit, giving us the least common instead
co2 = filter_bit_criteria(co2, pos, co2_bit);
}
}
// assume both vecs have len == 1 now, since otherwise it means
// AoC gave us an invalid problem input
oxy[0] as u64 * co2[0] as u64
}
pub fn run(data: &str) -> eyre::Result<()> {
let mut nums = Vec::new();
for line in data.lines() {
@ -43,5 +73,6 @@ pub fn run(data: &str) -> eyre::Result<()> {
}
println!("One: {}", part1(&nums));
println!("Two: {}", part2(&nums));
Ok(())
}