day 11
This commit is contained in:
157
day11/src/main.rs
Normal file
157
day11/src/main.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use std::fs::read_to_string;
|
||||
use std::iter::zip;
|
||||
use std::time::Instant;
|
||||
|
||||
fn naive_implementation(input_str: &str) -> (i64, i64) {
|
||||
let width = input_str.lines().next().unwrap().len();
|
||||
let height = input_str.lines().count();
|
||||
// These arrays will hold mapping of initial coordinates to coordinates after expansion
|
||||
// In the initial loop the elements will be 1 for empty and 0 for non-empty
|
||||
let mut x_mapping: Vec<i64> = vec![1; width];
|
||||
let mut y_mapping: Vec<i64> = vec![1; height];
|
||||
// List of tuples of galaxy coordinates
|
||||
let mut galaxies: Vec::<(i64, i64)> = vec![];
|
||||
for (j, line) in input_str.lines().enumerate() {
|
||||
for (i, c) in line.bytes().enumerate() {
|
||||
if c == b'#' {
|
||||
x_mapping[i] = 0;
|
||||
y_mapping[j] = 0;
|
||||
galaxies.push((i as i64, j as i64));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Convert mappings of 0 and 1 to accumulated coordinate mappings
|
||||
// mapping1 is for task 1, mapping2 is for task 2, mapping2 is a purely output argument
|
||||
let accumulate_mappings =
|
||||
|mapping1: &mut Vec<i64>, mapping2: &mut Vec<i64>| {
|
||||
let mut accmulator: i64 = 0;
|
||||
let mut accmulator2: i64 = 0;
|
||||
for (x1, x2) in zip(mapping1.iter_mut(), mapping2.iter_mut()) {
|
||||
accmulator += *x1;
|
||||
accmulator2 += *x1 * 999999;
|
||||
*x1 = accmulator;
|
||||
*x2 = accmulator2;
|
||||
accmulator += 1;
|
||||
accmulator2 += 1;
|
||||
}
|
||||
};
|
||||
// Convert galaxy coordinates using mapping arrays
|
||||
// Returns a copy becuase we need two different mappings for two tasks
|
||||
let map_galaxies =
|
||||
|galaxies: &Vec<(i64, i64)>, x_mapping: &Vec<i64>, y_mapping: &Vec<i64>| {
|
||||
galaxies.iter().map(|&(x, y)| (x_mapping[x as usize], y_mapping[y as usize])).collect::<Vec<_>>()
|
||||
};
|
||||
// Calculating the distances pairwise
|
||||
let sum_distances =
|
||||
|galaxies: &Vec<(i64, i64)>| {
|
||||
let mut sum1 = 0;
|
||||
for (i, &(x1, y1)) in galaxies.iter().enumerate() {
|
||||
for &(x2, y2) in galaxies[i + 1..].iter() {
|
||||
sum1 += (x2 - x1).abs() + (y2 - y1).abs();
|
||||
}
|
||||
}
|
||||
sum1
|
||||
};
|
||||
let mut x_mapping2: Vec<i64> = vec![0; width];
|
||||
let mut y_mapping2: Vec<i64> = vec![0; height];
|
||||
accumulate_mappings(&mut x_mapping, &mut x_mapping2);
|
||||
accumulate_mappings(&mut y_mapping, &mut y_mapping2);
|
||||
let galaxies_mapped1 = map_galaxies(&galaxies, &x_mapping, &y_mapping);
|
||||
let galaxies_mapped2 = map_galaxies(&galaxies, &x_mapping2, &y_mapping2);
|
||||
let sum1 = sum_distances(&galaxies_mapped1);
|
||||
let sum2 = sum_distances(&galaxies_mapped2);
|
||||
(sum1, sum2)
|
||||
}
|
||||
|
||||
fn improved_implementation(input_str: &str) -> (i64, i64) {
|
||||
let width = input_str.lines().next().unwrap().len();
|
||||
let height = input_str.lines().count();
|
||||
// These arrays will hold mapping of initial coordinates to coordinates after expansion
|
||||
// In the initial loop the elements will be 1 for empty and 0 for non-empty
|
||||
let mut x_mapping: Vec<i64> = vec![1; width];
|
||||
let mut y_mapping: Vec<i64> = vec![1; height];
|
||||
// These arrays hold a tupe of row/col index and count of galaxies in that index
|
||||
// They will be later mapped using mapping arrays
|
||||
let mut x_counters = (0..width)
|
||||
.map(|i| (i as i64, 0i64)).collect::<Vec<_>>();
|
||||
let mut y_counters = (0..height)
|
||||
.map(|i| (i as i64, 0i64)).collect::<Vec<_>>();
|
||||
for (j, line) in input_str.lines().enumerate() {
|
||||
for (i, c) in line.bytes().enumerate() {
|
||||
if c == b'#' {
|
||||
x_mapping[i] = 0;
|
||||
y_mapping[j] = 0;
|
||||
let (_, count_x) = &mut x_counters[i];
|
||||
*count_x += 1;
|
||||
let (_, count_y) = &mut y_counters[j];
|
||||
*count_y += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove empty rows/cols
|
||||
x_counters.retain(|(_, counter)| *counter > 0);
|
||||
y_counters.retain(|(_, counter)| *counter > 0);
|
||||
// Convert mappings of 0 and 1 to accumulated coordinate mappings
|
||||
// mapping1 is for task 1, mapping2 is for task 2, mapping2 is a purely output argument
|
||||
let accumulate_mappings =
|
||||
|mapping1: &mut Vec<i64>, mapping2: &mut Vec<i64>| {
|
||||
let mut accmulator: i64 = 0;
|
||||
let mut accmulator2: i64 = 0;
|
||||
for (x1, x2) in zip(mapping1.iter_mut(), mapping2.iter_mut()) {
|
||||
accmulator += *x1;
|
||||
accmulator2 += *x1 * 999999;
|
||||
*x1 = accmulator;
|
||||
*x2 = accmulator2;
|
||||
accmulator += 1;
|
||||
accmulator2 += 1;
|
||||
}
|
||||
};
|
||||
// Convert counter indices using mapping arrays
|
||||
// Returns a copy becuase we need two different mappings for two tasks
|
||||
let map_counter_coordinates =
|
||||
|counters: &Vec<(i64, i64)>, mapping: &Vec<i64>| {
|
||||
counters.iter().map(|&(i, counter)| (mapping[i as usize], counter)).collect::<Vec<_>>()
|
||||
};
|
||||
// Sum all distances along one dimension
|
||||
let sum_distances =
|
||||
|counters: &Vec<(i64, i64)>| {
|
||||
let mut sum1 = 0;
|
||||
for (i, &(pos1, counter1)) in counters.iter().enumerate() {
|
||||
for &(pos2, counter2) in counters[i + 1..].iter() {
|
||||
sum1 += (pos2 - pos1) * counter1 * counter2;
|
||||
}
|
||||
}
|
||||
sum1
|
||||
};
|
||||
let mut x_mapping2: Vec<i64> = vec![0; width];
|
||||
let mut y_mapping2: Vec<i64> = vec![0; height];
|
||||
accumulate_mappings(&mut x_mapping, &mut x_mapping2);
|
||||
accumulate_mappings(&mut y_mapping, &mut y_mapping2);
|
||||
let x_counters1 = map_counter_coordinates(&x_counters, &x_mapping);
|
||||
let y_counters1 = map_counter_coordinates(&y_counters, &y_mapping);
|
||||
let x_counters2 = map_counter_coordinates(&x_counters, &x_mapping2);
|
||||
let y_counters2 = map_counter_coordinates(&y_counters, &y_mapping2);
|
||||
let sum1 = sum_distances(&x_counters1) + sum_distances(&y_counters1);
|
||||
let sum2 = sum_distances(&x_counters2) + sum_distances(&y_counters2);
|
||||
(sum1, sum2)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let time_start = Instant::now();
|
||||
let input_str = read_to_string("input.txt").unwrap();
|
||||
let time_start_no_io = Instant::now();
|
||||
// Option to
|
||||
let mut sum1 = 0i64;
|
||||
let mut sum2 = 0i64;
|
||||
// Solve multiple times for performance testing
|
||||
for _ in 0..1000 {
|
||||
//(sum1, sum2) = naive_implementation(&input_str);
|
||||
(sum1, sum2) = improved_implementation(&input_str);
|
||||
}
|
||||
let elapsed = time_start.elapsed().as_micros();
|
||||
let elapsed_no_io = time_start_no_io.elapsed().as_micros();
|
||||
println!("Time: {}us", elapsed);
|
||||
println!("Time without file i/o: {}us", elapsed_no_io);
|
||||
println!("Sum1: {}", sum1);
|
||||
println!("Sum1: {}", sum2);
|
||||
}
|
Reference in New Issue
Block a user