optimize transpose

This commit is contained in:
kageru 2022-12-08 14:53:11 +01:00
parent 9e3e03bcd3
commit 9a26ae4a4d

View File

@ -1,4 +1,4 @@
#![feature(test)] #![feature(test, get_many_mut)]
extern crate test; extern crate test;
use itertools::Itertools; use itertools::Itertools;
use std::iter::repeat; use std::iter::repeat;
@ -28,9 +28,15 @@ fn is_visible_1d<'a>(iter: impl IntoIterator<Item = &'a u8>, rev: bool) -> Vec<b
} }
fn transpose<T: Copy>(v: &Vec<Vec<T>>) -> Vec<Vec<T>> { fn transpose<T: Copy>(v: &Vec<Vec<T>>) -> Vec<Vec<T>> {
let len = v.len(); let mut v = v.clone();
let mut iters: Vec<_> = v.iter().map(|n| n.iter()).collect(); let size = v.len();
(0..len).map(|_| iters.iter_mut().map(|i| i.next().unwrap()).copied().collect::<Vec<T>>()).collect() for i in 0..size {
for j in i + 1..size {
let [a, b] = v.get_many_mut([i, j]).unwrap();
std::mem::swap(&mut a[j], &mut b[i]);
}
}
v
} }
fn horizontally_visible(v: &[Vec<u8>]) -> Vec<Vec<bool>> { fn horizontally_visible(v: &[Vec<u8>]) -> Vec<Vec<bool>> {
@ -45,14 +51,15 @@ fn part1(parsed: &Parsed) -> usize {
fn part2(parsed: &Parsed) -> usize { fn part2(parsed: &Parsed) -> usize {
let size = parsed.len(); // input is always square let size = parsed.len(); // input is always square
let transposed = transpose(parsed);
(1..size - 1) (1..size - 1)
.flat_map(|i| repeat(i).zip(1..size - 1)) .flat_map(|i| repeat(i).zip(1..size - 1))
.map(|(i, j)| { .map(|(i, j)| {
let tree = parsed[i][j]; let tree = parsed[i][j];
let a = visible_trees(((i + 1)..size).map(|i| parsed[i][j]), tree); let a = visible_trees(tree, transposed[j][(i + 1)..size].iter());
let b = visible_trees((0..i).rev().map(|i| parsed[i][j]), tree); let b = visible_trees(tree, transposed[j][0..i].iter().rev());
let c = visible_trees(((j + 1)..size).map(|j| parsed[i][j]), tree); let c = visible_trees(tree, parsed[i][(j + 1)..size].iter());
let d = visible_trees((0..j).rev().map(|j| parsed[i][j]), tree); let d = visible_trees(tree, parsed[i][0..j].iter().rev());
a * b * c * d a * b * c * d
}) })
.max() .max()
@ -60,13 +67,17 @@ fn part2(parsed: &Parsed) -> usize {
} }
#[inline] // this inline actually saves ~40% runtime #[inline] // this inline actually saves ~40% runtime
fn visible_trees(heights: impl Iterator<Item = u8>, tree: u8) -> usize { fn visible_trees<'a, I: Iterator<Item = &'a u8>>(tree: u8, heights: I) -> usize {
let mut heights = heights.peekable(); let mut heights = heights.copied().peekable();
heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0) heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0)
} }
// These have to be separate variables because the test outputs are used as function names and
// can’t contain special characters.
#[cfg(test)] #[cfg(test)]
const TEST_OUTPUT: &[bool] = &[true, true, false, true, false]; const TEST_OUTPUT: &[bool] = &[true, true, false, true, false];
#[cfg(test)]
const TRANSPOSE_OUTPUT: &[[u8; 3]; 3] = &[[1, 4, 7], [2, 5, 8], [3, 6, 9]];
boilerplate! { boilerplate! {
TEST_INPUT == "30373 TEST_INPUT == "30373
@ -80,6 +91,7 @@ boilerplate! {
}, },
unittests: { unittests: {
is_visible_1d: { [1, 3, 2, 4, 2].iter(), false => TEST_OUTPUT, }, is_visible_1d: { [1, 3, 2, 4, 2].iter(), false => TEST_OUTPUT, },
transpose: { &vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]] => TRANSPOSE_OUTPUT },
}, },
bench1 == 1543, bench1 == 1543,
bench2 == 595080, bench2 == 595080,