Skip to content

Commit d6e4ce9

Browse files
committed
Improvements to solutions
1 parent a01948d commit d6e4ce9

4 files changed

Lines changed: 96 additions & 52 deletions

File tree

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ Day 08 Part 2: 8199963486
3838

3939
| Puzzle | Part 1 | Part 2 | Total |
4040
|-----------|-----------|-----------|-----------|
41-
| Day 01 | 11.597 µs | 8.8473 µs | 20.444 µs |
42-
| Day 02 | 667.49 ns | 5.0826 µs | 5.7501 µs |
43-
| Day 03 | 23.290 µs | 23.779 µs | 47.069 µs |
44-
| Day 04 | 51.885 µs | 430.92 µs | 482.81 µs |
45-
| Day 05 | 17.620 µs | 7.6199 µs | 25.240 µs |
46-
| Day 06 | 1.4939 µs | 5.6753 µs | 7.1692 µs |
47-
| Day 07 | 4.6171 ms | 189.55 µs | 4.8067 ms |
48-
| Day 08 | 1.4039 ms | 9.9354 ms | 11.339 ms |
49-
| **Total** | | | 16.734 ms |
41+
| Day 01 | 11.617 µs | 8.8003 µs | 20.417 µs |
42+
| Day 02 | 664.21 ns | 5.2121 µs | 5.8763 µs |
43+
| Day 03 | 25.014 µs | 24.474 µs | 49.488 µs |
44+
| Day 04 | 78.720 µs | 493.83 µs | 572.55 µs |
45+
| Day 05 | 16.584 µs | 7.9829 µs | 24.567 µs |
46+
| Day 06 | 1.4684 µs | 5.6648 µs | 7.1332 µs |
47+
| Day 07 | 4.5162 ms | 202.87 µs | 4.7191 ms |
48+
| Day 08 | 714.25 µs | 1.4487 ms | 2.1630 ms |
49+
| **Total** | | | 7.5621 ms |
5050

5151
Benchmarks were measured using `cargo bench` on an [AMD Ryzen 9 7950X processor](https://www.cpubenchmark.net/cpu.php?id=5031).
5252

benches/aoc_bench.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ make_day_bench!(day08_bench, day08, "08");
3131
criterion_group! {
3232
name = benches;
3333
config = Criterion::default()
34-
.sample_size(300)
35-
.measurement_time(Duration::from_secs(30))
36-
.nresamples(200_000);
34+
.sample_size(500)
35+
.measurement_time(Duration::from_secs(10))
36+
.nresamples(100_000)
37+
.configure_from_args();
3738
targets = day01_bench, day02_bench, day03_bench, day04_bench, day05_bench, day06_bench,
3839
day07_bench, day08_bench
3940
}

src/day07.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ impl Puzzle for Day {
2020
/// Auxiliary space complexity: O(N)
2121
fn solve_part_1(&self) -> String {
2222
let mut num_splits: u64 = 0;
23-
let mut beams: HashSet<Pos> = HashSet::new();
24-
let mut next: HashSet<Pos> = HashSet::new();
25-
beams.insert(self.start);
23+
let mut beams: Vec<Pos> = Vec::new();
24+
let mut next: Vec<Pos> = Vec::new();
25+
beams.push(self.start);
2626
for _ in self.start.0..self.last_splitter_row() {
2727
next.clear();
2828
for &(r, c) in &beams {
2929
let nr = r + 1;
3030
if self.splitters.iter().contains(&(nr, c)) {
3131
num_splits += 1;
32-
next.insert((nr, c - 1));
33-
next.insert((nr, c + 1));
32+
unique_push(&mut next, (nr, c - 1));
33+
unique_push(&mut next, (nr, c + 1));
3434
} else {
35-
next.insert((nr, c));
35+
unique_push(&mut next, (nr, c));
3636
}
3737
}
3838
std::mem::swap(&mut beams, &mut next);
@@ -72,6 +72,12 @@ impl Puzzle for Day {
7272

7373
type Pos = (usize, usize);
7474

75+
fn unique_push(vec: &mut Vec<Pos>, pos: Pos) {
76+
if vec.last() != Some(&pos) {
77+
vec.push(pos);
78+
}
79+
}
80+
7581
impl Day {
7682
pub fn create(input: &str) -> Box<dyn Puzzle> {
7783
let mut start: Option<Pos> = None;

src/day08.rs

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::puzzle::Puzzle;
2+
use std::collections::BinaryHeap;
23

34
pub struct Day {
45
points: Vec<Point>,
@@ -8,34 +9,52 @@ impl Puzzle for Day {
89
/// Connect the 1000 closest pairs, then multiply sizes of the 3 largest circuits.
910
///
1011
/// Time complexity: O(N^2)
11-
/// Auxiliary space complexity: O(N^2)
12+
/// Auxiliary space complexity: O(N)
1213
fn solve_part_1(&self) -> String {
1314
short_connections_product(&self.points, 1000).to_string()
1415
}
1516

1617
/// Keep connecting closest pairs until all junction boxes are in one circuit.
1718
/// Return product of X coordinates of the last edge that merges the final two components.
1819
///
19-
/// Time complexity: O(N^2 log N)
20-
/// Auxiliary space complexity: O(N^2)
20+
/// Time complexity: O(N^2)
21+
/// Auxiliary space complexity: O(N)
2122
fn solve_part_2(&self) -> String {
2223
let n = self.points.len();
23-
let mut edges = all_edges(&self.points);
24-
edges.sort_unstable_by_key(|e| e.dist2);
25-
let mut dsu = Dsu::new(n);
26-
let mut last_merged: Option<Edge> = None;
27-
for e in edges {
28-
if dsu.union(e.from, e.to) {
29-
last_merged = Some(e);
30-
if dsu.components == 1 {
31-
break;
24+
let mut in_mst = vec![false; n];
25+
let mut best = vec![u64::MAX; n];
26+
let mut parent: Vec<Option<usize>> = vec![None; n];
27+
let mut max_edge: Option<(u64, usize)> = None;
28+
best[0] = 0;
29+
for _ in 0..n {
30+
let v = (0..n)
31+
.filter(|&i| !in_mst[i])
32+
.min_by_key(|&i| best[i])
33+
.unwrap();
34+
let v_best = best[v];
35+
in_mst[v] = true;
36+
if parent[v].is_some() {
37+
match max_edge {
38+
None => max_edge = Some((v_best, v)),
39+
Some((d, _)) if v_best > d => max_edge = Some((v_best, v)),
40+
_ => {}
41+
}
42+
}
43+
for u in 0..n {
44+
if !in_mst[u] {
45+
let d = self.points[v].dist2(&self.points[u]);
46+
if d < best[u] {
47+
best[u] = d;
48+
parent[u] = Some(v);
49+
}
3250
}
3351
}
3452
}
35-
let e = last_merged.unwrap();
36-
let p1_x = self.points[e.from].x;
37-
let p2_x = self.points[e.to].x;
38-
(p1_x * p2_x).to_string()
53+
let (_, v) = max_edge.unwrap();
54+
let p = parent[v].unwrap();
55+
let a = self.points[v].x as i128;
56+
let b = self.points[p].x as i128;
57+
(a * b).to_string()
3958
}
4059
}
4160

@@ -72,26 +91,25 @@ impl Point {
7291
}
7392
}
7493

94+
#[derive(Eq, PartialEq)]
7595
struct Edge {
7696
from: usize,
7797
to: usize,
7898
dist2: u64,
7999
}
80100

81-
fn all_edges(points: &[Point]) -> Vec<Edge> {
82-
let n = points.len();
83-
let mut edges = Vec::with_capacity(n * (n - 1) / 2);
84-
for i in 0..n {
85-
for j in (i + 1)..n {
86-
let dist2 = points[i].dist2(&points[j]);
87-
edges.push(Edge {
88-
from: i,
89-
to: j,
90-
dist2,
91-
});
92-
}
101+
impl Ord for Edge {
102+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
103+
self.dist2
104+
.cmp(&other.dist2)
105+
.then_with(|| self.from.cmp(&other.from))
106+
.then_with(|| self.to.cmp(&other.to))
107+
}
108+
}
109+
impl PartialOrd for Edge {
110+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
111+
Some(self.cmp(other))
93112
}
94-
edges
95113
}
96114

97115
struct Dsu {
@@ -154,16 +172,35 @@ impl Dsu {
154172

155173
fn short_connections_product(points: &[Point], count: usize) -> usize {
156174
let n = points.len();
157-
let mut edges = all_edges(points);
158-
let k = count.min(edges.len());
159-
edges.select_nth_unstable_by_key(k - 1, |e| e.dist2);
160-
edges.truncate(k);
175+
let mut edges: BinaryHeap<Edge> = BinaryHeap::with_capacity(count + 1);
176+
for i in 0..n {
177+
for j in (i + 1)..n {
178+
let d = points[i].dist2(&points[j]);
179+
if edges.len() < count {
180+
edges.push(Edge {
181+
from: i,
182+
to: j,
183+
dist2: d,
184+
});
185+
} else if let Some(top) = edges.peek() {
186+
if d < top.dist2 {
187+
edges.pop();
188+
edges.push(Edge {
189+
from: i,
190+
to: j,
191+
dist2: d,
192+
});
193+
}
194+
}
195+
}
196+
}
161197
let mut dsu = Dsu::new(n);
162198
for e in edges {
163199
dsu.union(e.from, e.to);
164200
}
165201
let mut sizes = dsu.component_sizes();
166-
sizes.sort_unstable_by(|a, b| b.cmp(a));
202+
sizes.select_nth_unstable_by_key(2, |&x| std::cmp::Reverse(x));
203+
sizes.truncate(3);
167204
sizes.into_iter().take(3).product()
168205
}
169206

0 commit comments

Comments
 (0)