day2: Use a LUT for pow10 operations for 50% performance improvement
This commit is contained in:
33
src/day2.rs
33
src/day2.rs
@@ -2,6 +2,19 @@ use aoc_runner_derive::{aoc, aoc_generator};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
const POW10MAX: usize = u64::MAX.ilog10() as usize;
|
||||||
|
const POW10: [u64; POW10MAX] = pow10_lut();
|
||||||
|
|
||||||
|
const fn pow10_lut<const N: usize>() -> [u64; N] {
|
||||||
|
let mut res = [0; N];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < N {
|
||||||
|
res[i] = 10u64.pow(i as u32);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
#[aoc_generator(day2)]
|
#[aoc_generator(day2)]
|
||||||
fn parse(input: &str) -> Vec<RangeInclusive<u64>> {
|
fn parse(input: &str) -> Vec<RangeInclusive<u64>> {
|
||||||
input
|
input
|
||||||
@@ -62,12 +75,12 @@ fn part1_arithmetic_brute(input: &[RangeInclusive<u64>]) -> u64 {
|
|||||||
for r in input {
|
for r in input {
|
||||||
// println!("Range: {:?}", r);
|
// println!("Range: {:?}", r);
|
||||||
for product in r.clone() {
|
for product in r.clone() {
|
||||||
let n_digits = product.ilog10() + 1;
|
let n_digits = (product.ilog10() + 1) as usize;
|
||||||
if n_digits % 2 != 0 {
|
if n_digits % 2 != 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let decade = 10u64.pow(n_digits / 2);
|
let decade = POW10[n_digits / 2];
|
||||||
let lhs = product / decade;
|
let lhs = product / decade;
|
||||||
let rhs = product % decade;
|
let rhs = product % decade;
|
||||||
// println!("{product} {n_digits}: D:{decade} L:{lhs} R:{rhs}");
|
// println!("{product} {n_digits}: D:{decade} L:{lhs} R:{rhs}");
|
||||||
@@ -84,21 +97,19 @@ fn part2_arithmetic_brute(input: &[RangeInclusive<u64>]) -> u64 {
|
|||||||
let mut invalid_sum = 0;
|
let mut invalid_sum = 0;
|
||||||
for r in input {
|
for r in input {
|
||||||
for product in r.clone() {
|
for product in r.clone() {
|
||||||
let n_digits = product.ilog10() + 1;
|
let n_digits = (product.ilog10() + 1) as usize;
|
||||||
|
|
||||||
for n in 1..=n_digits / 2 {
|
for n in 1..=n_digits / 2 {
|
||||||
let repetitions = n_digits / n;
|
let repetitions = n_digits / n;
|
||||||
if n_digits % n != 0 {
|
if n_digits % n != 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let decade = 10u64.pow(n_digits - n);
|
let decade = POW10[n_digits - n];
|
||||||
let lhs = product / decade;
|
let lhs = product / decade;
|
||||||
let remainder = product % decade;
|
let remainder = product % decade;
|
||||||
|
|
||||||
// for each repetition we multiply by 10^(rep * n)
|
// for each repetition we multiply by 10^(rep * n)
|
||||||
let expected_remainder = (0..repetitions - 1)
|
let expected_remainder = (0..repetitions - 1).map(|rep| lhs * POW10[rep * n]).sum();
|
||||||
.map(|rep| lhs * 10u64.pow(rep * n))
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
if remainder == expected_remainder {
|
if remainder == expected_remainder {
|
||||||
invalid_sum += product;
|
invalid_sum += product;
|
||||||
@@ -114,19 +125,19 @@ fn part2_arithmetic_brute(input: &[RangeInclusive<u64>]) -> u64 {
|
|||||||
fn part1_clever(input: &[RangeInclusive<u64>]) -> u64 {
|
fn part1_clever(input: &[RangeInclusive<u64>]) -> u64 {
|
||||||
let mut invalid_sum = 0;
|
let mut invalid_sum = 0;
|
||||||
for r in input {
|
for r in input {
|
||||||
let n_digits = r.start().ilog10() + 1;
|
let n_digits = (r.start().ilog10() + 1) as usize;
|
||||||
|
|
||||||
let mut lhs = if n_digits % 2 != 0 {
|
let mut lhs = if n_digits % 2 != 0 {
|
||||||
// If the number of digits is odd, we start our guess at the first possibility in the next decade
|
// If the number of digits is odd, we start our guess at the first possibility in the next decade
|
||||||
10u64.pow(n_digits / 2)
|
POW10[n_digits / 2]
|
||||||
} else {
|
} else {
|
||||||
let start_decade = 10u64.pow(n_digits / 2);
|
let start_decade = POW10[n_digits / 2];
|
||||||
r.start() / start_decade
|
r.start() / start_decade
|
||||||
};
|
};
|
||||||
|
|
||||||
// we will guess the next repeater, and check if it's in range, if not we are done.
|
// we will guess the next repeater, and check if it's in range, if not we are done.
|
||||||
loop {
|
loop {
|
||||||
let repeater = lhs * 10u64.pow(lhs.ilog10() + 1) + lhs;
|
let repeater = lhs * POW10[(lhs.ilog10() + 1) as usize] + lhs;
|
||||||
if repeater < *r.start() {
|
if repeater < *r.start() {
|
||||||
lhs += 1
|
lhs += 1
|
||||||
} else if r.contains(&repeater) {
|
} else if r.contains(&repeater) {
|
||||||
|
|||||||
Reference in New Issue
Block a user