This commit is contained in:
Acvaxoort
2023-12-12 01:30:21 +01:00
parent f1f6f0f79c
commit 772e220f46
4 changed files with 312 additions and 0 deletions

157
day11/src/main.rs Normal file
View 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);
}