Skip to content

Commit

Permalink
simple optimizations to reduce allocations and use better hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
kcaffrey committed Dec 26, 2023
1 parent 2e8b6c5 commit 5f10657
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 27 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
| [Day 22](./src/bin/22.rs) | `78.3µs` | `194.8µs` |
| [Day 23](./src/bin/23.rs) | `330.4µs` | `13.3ms` |
| [Day 24](./src/bin/24.rs) | `257.2µs` | `16.2µs` |
| [Day 25](./src/bin/25.rs) | `2.1ms` | `-` |
| [Day 25](./src/bin/25.rs) | `947.0µs` | `-` |

**Total: 32.61ms**
**Total: 31.46ms**
<!--- benchmarking table --->

---
Expand Down
56 changes: 31 additions & 25 deletions src/bin/25.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,59 @@
use std::collections::{HashMap, HashSet, VecDeque};
use std::collections::VecDeque;

use fxhash::{FxHashMap, FxHashSet};

advent_of_code::solution!(25, 1);

pub fn part_one(input: &str) -> Option<usize> {
let graph = parse_graph(input);
let mut flow: NetworkFlow = parse_graph(input).into();
let s = 0;
(1..graph.vertices).find_map(|t| find_cut_of_size(&graph, s, t, 3))
(1..flow.vertices).find_map(|t| find_cut_of_size(&mut flow, s, t, 3))
}

fn find_cut_of_size(graph: &Graph, s: usize, t: usize, cut: i16) -> Option<usize> {
let mut net: NetworkFlow = graph.into();
let mut queue = VecDeque::new();
fn find_cut_of_size(net: &mut NetworkFlow, s: usize, t: usize, cut: i16) -> Option<usize> {
net.flow.fill(0);

let mut flow = 0;
let mut pred = vec![None; net.vertices];
while flow <= cut {
// Find the shortest path from s to t.
pred.fill(None);
queue.clear();
queue.push_back(s);
net.pred.fill(None);
net.queue.clear();
net.queue.push_back(s);
let mut seen_vertices = 0;
while let Some(cur) = queue.pop_front() {
if pred[t].is_some() {
while let Some(cur) = net.queue.pop_front() {
if net.pred[t].is_some() {
break;
}
seen_vertices += 1;
for &next in &graph.adjacency[cur] {
for &next in &net.adjacency[cur] {
if next != s
&& pred[next].is_none()
&& net.pred[next].is_none()
&& net.capacity(cur, next) > net.flow(cur, next)
{
pred[next] = Some(cur);
queue.push_back(next);
net.pred[next] = Some(cur);
net.queue.push_back(next);
}
}
}

// If there was no path, and the cut was the right size,
// return an answer.
if pred[t].is_none() {
return (flow <= cut).then_some(seen_vertices * (graph.vertices - seen_vertices));
if net.pred[t].is_none() {
return (flow <= cut).then_some(seen_vertices * (net.vertices - seen_vertices));
}

// If we found a path, find the min flow along the path that we will use to update the
// flow for the residual.
let mut df = i16::MAX;
let mut cur = t;
while let Some(prev) = pred[cur] {
while let Some(prev) = net.pred[cur] {
df = df.min(net.capacity(prev, cur) - net.flow(prev, cur));
cur = prev;
}

// Update the residual flow.
let mut cur = t;
while let Some(prev) = pred[cur] {
while let Some(prev) = net.pred[cur] {
net.add_flow(prev, cur, df);
net.add_flow(cur, prev, -df);
cur = prev;
Expand Down Expand Up @@ -81,13 +81,13 @@ fn parse_graph(input: &str) -> Graph {
#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct Graph {
vertices: usize,
adjacency: Vec<HashSet<usize>>,
adjacency: Vec<FxHashSet<usize>>,
}

#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct GraphBuilder<'a> {
graph: Graph,
vertex_ids: HashMap<&'a str, usize>,
vertex_ids: FxHashMap<&'a str, usize>,
}

impl<'a> GraphBuilder<'a> {
Expand Down Expand Up @@ -115,11 +115,14 @@ impl<'a> GraphBuilder<'a> {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct NetworkFlow {
vertices: usize,
adjacency: Vec<FxHashSet<usize>>,
capacity: Vec<i16>,
flow: Vec<i16>,
pred: Vec<Option<usize>>,
queue: VecDeque<usize>,
}

impl NetworkFlow {
Expand All @@ -136,18 +139,21 @@ impl NetworkFlow {
}
}

impl From<&Graph> for NetworkFlow {
fn from(graph: &Graph) -> Self {
impl From<Graph> for NetworkFlow {
fn from(graph: Graph) -> Self {
let mut ret = Self {
vertices: graph.vertices,
capacity: vec![0; graph.vertices * graph.vertices],
flow: vec![0; graph.vertices * graph.vertices],
pred: vec![None; graph.vertices],
..Default::default()
};
for v0 in 0..ret.vertices {
for &v1 in &graph.adjacency[v0] {
ret.capacity[v0 * ret.vertices + v1] = 1;
}
}
ret.adjacency = graph.adjacency;
ret
}
}
Expand Down

0 comments on commit 5f10657

Please sign in to comment.