Improved Logic for 2021 day 24 (apply bounds to individual digits instead of looping through integers until we find a suitable one)

This commit is contained in:
Burnus 2023-05-12 16:06:19 +02:00
parent 68c74b45b0
commit 8a8085b8b5

View file

@ -4,33 +4,61 @@ pub fn run() -> (usize, usize) {
(first, second)
}
fn next_valid_input(previous: usize, ascending: bool) -> usize {
let mut curr = if ascending {
previous+1
} else {
previous-1
/// Takes a 6 digit seed (since we can somewhat freely chose 6 of the 14 digits) and a bool
/// indicating wether we want the next higher (true), or lower (false) valid input. Produces said
/// input. If seed is not 6 digits long, it will be clamped to be (so next_valid_input(0, true)
/// will produce the lowest valid input, and next_valid_input(1_000_000, false) will produce the
/// highest).
fn next_valid_input(seed: usize, ascending: bool) -> usize {
// Make sure we have a 6 digit seed to begin with (containing no 0s, as per spec)
let curr = match ascending {
true => (seed+1).clamp(111_111, 999_999),
false => (seed-1).clamp(111_111, 999_999),
};
// We only need to guess 6 digits and can derive the rest from them, as shown below
curr = curr.clamp(111_111, 999_999);
// The seed digits need to fall into the following ranges, so we can add/substract the
// calculated offsets for the other digits later. The digits are considered right to left,
// which makes it easier for me to splice them into an array and manipulate them accordingly.
let digit_ranges = [
(1, 6),
(7, 9),
(2, 9),
(6, 9),
(4, 9),
(1, 2),
];
let mut digits: Vec<isize>;
loop {
digits = Vec::new();
let mut rest = curr;
while rest > 0 {
let digit = rest % 10;
if digit == 0 {
if ascending {
curr += 10_usize.pow(digits.len() as u32);
} else {
curr -= 10_usize.pow(digits.len() as u32);
}
digits = Vec::new();
rest = curr;
} else {
digits.push(digit as isize);
rest /= 10;
}
if ascending {
for idx in 0..digits.len() {
let digit = digits[idx];
digits[idx] = digit.max(digit_ranges[idx].0);
if digit > digit_ranges[idx].1 {
digits[idx] = digit_ranges[idx].1;
for i in 0..idx {
digits[i] = digit_ranges[i].0;
}
}
}
} else {
for idx in 0..digits.len() {
let digit = digits[idx];
digits[idx] = digit.min(digit_ranges[idx].1);
if digit < digit_ranges[idx].0 {
digits[idx] = digit_ranges[idx].0;
for i in 0..idx {
digits[i] = digit_ranges[i].1;
}
}
}
}
digits.reverse();
// From carefully observing the input code, we know that half the digits can be linearly
// derived from the other half, using the following conversion (where [n] denotes the nth
// most significant digit of the number):
@ -56,8 +84,7 @@ fn next_valid_input(previous: usize, ascending: bool) -> usize {
digits[1]-3,
digits[0]+7,
];
if other_digits.iter().all(|d| (1..10).contains(d)) {
return 10_usize.pow(13) * digits[0] as usize +
10_usize.pow(13) * digits[0] as usize +
10_usize.pow(12) * digits[1] as usize +
10_usize.pow(11) * digits[2] as usize +
10_usize.pow(10) * 9 +
@ -70,16 +97,7 @@ fn next_valid_input(previous: usize, ascending: bool) -> usize {
10_usize.pow(3) * other_digits[2] as usize +
10_usize.pow(2) * other_digits[3] as usize +
10 * other_digits[4] as usize +
other_digits[5] as usize;
} else {
if ascending {
curr += 1;
} else {
curr -= 1;
}
continue;
}
}
other_digits[5] as usize
}
#[cfg(test)]