Skip to content

Commit d075301

Browse files
committed
Optimize 2024 day 14 using Chinese reminder theorem
1 parent ae932be commit d075301

File tree

1 file changed

+27
-33
lines changed

1 file changed

+27
-33
lines changed

crates/year2024/src/day14.rs

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
use std::sync::atomic::{AtomicI32, Ordering};
2-
use utils::multithreading::worker_pool;
1+
use utils::number::chinese_remainder;
32
use utils::point::Point2D;
43
use utils::prelude::*;
54

65
/// Finding when robots arrange themselves into a picture.
7-
///
8-
/// Assumes that the picture of the Christmas tree will involve a horizontal line of at least 10
9-
/// robots, and that doesn't happen in any prior iterations.
106
#[derive(Clone, Debug)]
117
pub struct Day14 {
128
robots: Vec<Robot>,
@@ -18,7 +14,6 @@ struct Robot {
1814
velocity: Point2D<i32>,
1915
}
2016

21-
// WIDTH must be less than 128
2217
const WIDTH: i32 = 101;
2318
const HEIGHT: i32 = 103;
2419

@@ -64,39 +59,38 @@ impl Day14 {
6459

6560
#[must_use]
6661
pub fn part2(&self) -> u32 {
67-
let counter = AtomicI32::new(0);
68-
let result = AtomicI32::new(i32::MAX);
62+
// The only time there are more than 30 robots in a single row and more than 30 robots in a
63+
// single column is when the robots are arranged into a Christmas Tree. Additionally, each
64+
// robot's x position repeats every 101 seconds, and y position repeats every 103 seconds.
65+
// This allows separately finding the time mod 101 where there are enough robots in a single
66+
// column, and then finding the time mod 103 where there are enough robots in a single row.
67+
// This then gives us two equations which can be solved using the Chinese reminder theorem.
68+
// result % 101 == A
69+
// result % 103 == B
6970

70-
worker_pool(|| {
71-
while result.load(Ordering::Acquire) == i32::MAX {
72-
let i = counter.fetch_add(1, Ordering::AcqRel);
73-
let mut grid = [0u128; HEIGHT as usize];
71+
let a = (0..WIDTH)
72+
.find(|&t| {
73+
let mut columns = [0u8; WIDTH as usize];
7474
for r in &self.robots {
75-
let pos = r.position + (r.velocity * i);
76-
grid[pos.y.rem_euclid(HEIGHT) as usize] |= 1 << pos.x.rem_euclid(WIDTH);
75+
let col = (r.position.x + r.velocity.x * t).rem_euclid(WIDTH);
76+
columns[col as usize] += 1;
7777
}
78+
columns.iter().any(|&b| b >= 30)
79+
})
80+
.expect("expected a time to have more than 30 robots in a column");
7881

79-
if grid.iter().any(|&b| Self::has_ten_consecutive_bits(b)) {
80-
result.fetch_min(i, Ordering::AcqRel);
81-
return;
82+
let b = (0..HEIGHT)
83+
.find(|&t| {
84+
let mut rows = [0u8; HEIGHT as usize];
85+
for r in &self.robots {
86+
let row = (r.position.y + r.velocity.y * t).rem_euclid(HEIGHT);
87+
rows[row as usize] += 1;
8288
}
83-
}
84-
});
85-
86-
result.into_inner() as u32
87-
}
89+
rows.iter().any(|&b| b >= 30)
90+
})
91+
.expect("expected a time to have more than 30 robots in a row");
8892

89-
fn has_ten_consecutive_bits(b: u128) -> bool {
90-
b & (b << 1)
91-
& (b << 2)
92-
& (b << 3)
93-
& (b << 4)
94-
& (b << 5)
95-
& (b << 6)
96-
& (b << 7)
97-
& (b << 8)
98-
& (b << 9)
99-
!= 0
93+
chinese_remainder([a, b], [WIDTH, HEIGHT]).unwrap() as u32
10094
}
10195
}
10296

0 commit comments

Comments
 (0)