UNPKG

@cedoor/nfa

Version:

TypeScript implementation of some network flow algorithms.

81 lines (68 loc) 3.22 kB
import Graph, { GraphData } from "../dataStructures/graph" import Node from "../dataStructures/node" import Arc from "../dataStructures/arc" import bfs from "../searchAlgorithms/bfs" import { getResidualCapacity, sendFlow, getResidualGraph, getOptimalGraph } from "../utils" /** * The Edmonds–Karp algorithm is an implementation of the Ford–Fulkerson * method for computing the maximum flow between two nodes in a flow network. * The algorithm uses the Breadth-First Search algorithm to find the * augmenting path between the source and the sink node, and increment * the maximum flow with the minimum arc capacity for each path * using the residual graph. Returns the optimal graph, the maximum flow, the source and the sink nodes. * Time complexity: O(n * m^2). * @param {Graph | GraphData} The graph to visit. * @returns {[Graph, number, number, number]} The optimal flow graph, the maximum flow, the source and sink nodes. */ export default function edmondsKarp(graph: Graph | GraphData): [Graph, number, number, number] { if (!(graph instanceof Graph)) { graph = new Graph(graph) } // Extends the graph to calculate the feasible graph. const [tSourceNodeId, tSinkNodeId] = extendGraph(graph) const residualGraph = getResidualGraph(graph) let maximumflow = 0 let path = bfs(residualGraph, tSourceNodeId, tSinkNodeId) // While loop stops when there is no path between the source and sink nodes. while (path && Array.isArray(path)) { const residualCapacity = getResidualCapacity(residualGraph, path) maximumflow += residualCapacity sendFlow(residualGraph, path, residualCapacity) // Searches another path with the new residual graph. path = bfs(residualGraph, tSourceNodeId, tSinkNodeId) } return [getOptimalGraph(residualGraph), maximumflow, tSourceNodeId, tSinkNodeId] } /** * Extends the graph with two new source and sink nodes. * For each node with balance greater than 0, it creates * an arc with capacity = balance from the new source node to this node. * For each node with balance less than 0, it creates * an arc with capacity = -balance from the node to the new sink nodes. * This allows you to compute a feasible graph, where the mass balance * constraint is respected. * Time complexity: O(n). * @param {Graph} The graph to extend. * @returns {number[]} The new source and sink nodes. */ function extendGraph(graph: Graph): [number, number] { const nodes = graph.getNodes() const maxId = Math.max(...nodes.map((node) => node.id)) const newSourceNode = new Node(maxId + 1, 0) const newSinkNode = new Node(maxId + 2, 0) graph.addNode(newSourceNode) graph.addNode(newSinkNode) for (const node of nodes) { if (node.balance > 0) { const arc = new Arc(node.id, 0, node.balance) newSourceNode.addArc(arc) newSourceNode.balance += node.balance } else if (node.balance < 0) { const arc = new Arc(newSinkNode.id, 0, -node.balance) node.addArc(arc) newSinkNode.balance += node.balance } node.balance = 0 } return [newSourceNode.id, newSinkNode.id] }