From 5f106571a6b7f15ef13f1661f63f17ee5bb66602 Mon Sep 17 00:00:00 2001 From: Kevin Caffrey Date: Mon, 25 Dec 2023 21:34:18 -0500 Subject: [PATCH] simple optimizations to reduce allocations and use better hashing --- README.md | 4 ++-- src/bin/25.rs | 56 ++++++++++++++++++++++++++++----------------------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c258e2d..d2aadfe 100644 --- a/README.md +++ b/README.md @@ -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** --- diff --git a/src/bin/25.rs b/src/bin/25.rs index 744fdd3..a72af6f 100644 --- a/src/bin/25.rs +++ b/src/bin/25.rs @@ -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 { - 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 { - 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 { + 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; @@ -81,13 +81,13 @@ fn parse_graph(input: &str) -> Graph { #[derive(Debug, Default, Clone, PartialEq, Eq)] struct Graph { vertices: usize, - adjacency: Vec>, + adjacency: Vec>, } #[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> { @@ -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>, capacity: Vec, flow: Vec, + pred: Vec>, + queue: VecDeque, } impl NetworkFlow { @@ -136,18 +139,21 @@ impl NetworkFlow { } } -impl From<&Graph> for NetworkFlow { - fn from(graph: &Graph) -> Self { +impl From 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 } }