/// <summary> /// Uses a djikstra-like algorithm to flood the graph from the start /// node until the target node is found. /// All visited nodes have their distance from the start node updated. /// </summary> /// <param name="start">The starting node.</param> /// <param name="target">The target node.</param> /// <param name="front">The last newly found nodes.</param> /// <param name="visited">The already visited nodes.</param> /// <param name="distFromStart">The traversed distance from the /// starting node in edges.</param> /// <returns>The distance from the start node to the target node.</returns> /// <remarks> - Currently the target node is never found if contained /// in front or visited. /// - If front = { start }, then distFromStart should be 0.</remarks> public int dijkstraStep(GraphNode start, GraphNode target, HashSet<GraphNode> front, HashSet<GraphNode> visited, int distFromStart) { HashSet<GraphNode> newFront = new HashSet<GraphNode>(); HashSet<GraphNode> newVisited = new HashSet<GraphNode>(visited); newVisited.Concat(front); foreach (GraphNode node in front) { newVisited.Add(node); foreach (GraphNode adjacentNode in node.Adjacent) { if (adjacentNode == target) return distFromStart + 1; // Could be combined in newVisited... if (visited.Contains(adjacentNode)) continue; if (front.Contains(adjacentNode)) continue; newFront.Add(adjacentNode); } } // This wouldn't need recursion, but it's more convenient this way. if (newFront.Count > 0) return dijkstraStep(start, target, newFront, newVisited, distFromStart + 1); throw new GraphNotConnectedException(); }
/// <summary> /// Uses a djikstra-like algorithm to flood the graph from the start /// node until the target node is found. /// </summary> /// <param name="start">The starting node.</param> /// <param name="target">The target node.</param> /// <param name="front">The last newly found nodes.</param> /// <param name="visited">The already visited nodes.</param> /// <param name="predecessors">The dictionary of the predecessors of the visited nodes.</param> /// <param name="distFromStart">The traversed distance from the /// starting node in edges.</param> /// <returns>A tuple containing the distance from the start node to the target node and the shortest path.</returns> /// <remarks> - Currently the target node is never found if contained /// in front or visited. /// - If front = { start }, then distFromStart should be 0.</remarks> public Tuple<int, ushort[]> dijkstraStep(GraphNode start, GraphNode target, HashSet<GraphNode> front, HashSet<GraphNode> visited, Dictionary<ushort, ushort> predecessors, int distFromStart) { HashSet<GraphNode> newFront = new HashSet<GraphNode>(); visited.UnionWith(front); foreach (GraphNode node in front) { foreach (GraphNode adjacentNode in node.Adjacent) { if (adjacentNode == target) { int length = distFromStart + 1; predecessors.Add(adjacentNode.Id, node.Id); ushort[] path = generateShortestPath(start.Id, target.Id, predecessors, length); return new Tuple<int, ushort[]>(length, path); } // newFront check is necessary because the dictionary complains about duplicates if (visited.Contains(adjacentNode) || newFront.Contains(adjacentNode)) continue; newFront.Add(adjacentNode); predecessors.Add(adjacentNode.Id, node.Id); } } // This wouldn't need recursion, but it's more convenient this way. if (newFront.Count > 0) return dijkstraStep(start, target, newFront, visited, predecessors, distFromStart + 1); throw new GraphNotConnectedException(); }
/// <summary> /// Uses a djikstra-like algorithm to flood the graph from the start /// node until the target node is found. /// All visited nodes have their distance from the start node updated. /// </summary> /// <param name="start">The starting node.</param> /// <param name="target">The target node.</param> /// <returns>The distance from the start node to the target node.</returns> public int Dijkstra(GraphNode start, GraphNode target) { if (start == target) return 0; return dijkstraStep(start, target, new HashSet<GraphNode>() { start }, new HashSet<GraphNode>(), 0); }
private void setDistance(GraphNode a, GraphNode b, int distance) { uint index = getIndex(a, b); if (!_distances.ContainsKey(index)) _distances.Add(index, distance); }
/// <summary> /// Compounds two ushort node indices into a single uint one, which /// is independent of the order of the two indices. /// </summary> /// <param name="a">The first index.</param> /// <param name="b">The second index.</param> /// <returns>The compounded index.</returns> private uint getIndex(GraphNode a, GraphNode b) { ushort aI = a.Id; ushort bI = b.Id; return (uint)(Math.Min(aI, bI) << 16) + (uint)(Math.Max(aI, bI)); }
/// <summary> /// Retrieves the path distance from one node to another, or calculates /// it if it has not yet been found. /// </summary> /// <param name="a">The first graph node.</param> /// <param name="b">The second graph node</param> /// <returns>The length of the path from a to b (equals the amount of edges /// traversed).</returns> public int GetDistance(GraphNode a, GraphNode b) { uint index = getIndex(a, b); // If we already calculated the shortest path, use that. if (_distances.ContainsKey(index)) return _distances[index]; // Otherwise, use pathfinding to find it... int pathLength = Dijkstra(a, b); //... and save it... _distances.Add(index, pathLength); // ...and return it. return pathLength; }
public GraphEdge(GraphNode inside, GraphNode outside) { this.inside = inside; this.outside = outside; }
/// <summary> /// Uses a djikstra-like algorithm to flood the graph from the start /// node until the target node is found. /// </summary> /// <param name="start">The starting node.</param> /// <param name="target">The target node.</param> /// <returns>A tuple containing the distance from the start node to the target node and the shortest path.</returns> public Tuple<int, ushort[]> Dijkstra(GraphNode start, GraphNode target) { if (start == target) return new Tuple<int, ushort[]>(0, new ushort[0]); return dijkstraStep(start, target, new HashSet<GraphNode>() { start }, new HashSet<GraphNode>(), new Dictionary<ushort, ushort>(), 0); }
private Tuple<int, ushort[]> runAndSaveDijkstra(GraphNode start, GraphNode target) { var index = getIndex(start, target); if (_distances.ContainsKey(index)) return new Tuple<int, ushort[]>(_distances[index], _paths[index]); var result = Dijkstra(start, target); _distances.Add(index, result.Item1); _paths.Add(index, result.Item2); return result; }
/// <summary> /// Retrieves the shortest path from one node to another, or calculates /// it if it has not yet been found. /// </summary> /// <param name="a">The first graph node.</param> /// <param name="b">The second graph node</param> /// <returns>The shortest path from a to b, not containing either and ordered from b to a.</returns> public ushort[] GetShortestPath(GraphNode a, GraphNode b) { uint index = getIndex(a, b); if (_paths.ContainsKey(index)) { return _paths[index]; } return runAndSaveDijkstra(a, b).Item2; }
/// <summary> /// Retrieves the path distance from one node to another, or calculates /// it if it has not yet been found. /// </summary> /// <param name="a">The first graph node.</param> /// <param name="b">The second graph node</param> /// <returns>The length of the path from a to b (equals the amount of edges /// traversed).</returns> public int GetDistance(GraphNode a, GraphNode b) { uint index = getIndex(a, b); // If we already calculated the shortest path, use that. if (_distances.ContainsKey(index)) return _distances[index]; return runAndSaveDijkstra(a, b).Item1; }
/// <summary> /// Uses Prim's algorithm to build an MST spanning the mstNodes. /// </summary> /// <param name="startFrom">A GraphNode to start from.</param> /// <returns>A list of GraphEdges forming the MST.</returns> public List<GraphEdge> Span(GraphNode startFrom) { /// With n nodes, we can have up to n (actually n-1) edges adjacent to each node. HeapPriorityQueue<GraphEdge> adjacentEdgeQueue = new HeapPriorityQueue<GraphEdge>(mstNodes.Count * mstNodes.Count); /// Removing all edges that satisfy a property (here a certain "outside" /// node) from the queue is not actually trivial, since you could only /// iterate over all entries (and you want to avoid that) if you don't /// have the references to the edges at hand. /// I guess this is the easiest way to do it... Dictionary<GraphNode, List<GraphEdge>> edgesLeadingToNode = new Dictionary<GraphNode, List<GraphEdge>>(); foreach (GraphNode node in mstNodes) edgesLeadingToNode[node] = new List<GraphEdge>(); // All nodes that are already included. HashSet<GraphNode> inMst = new HashSet<GraphNode>(); // All nodes that are not yet included. HashSet<GraphNode> toAdd = new HashSet<GraphNode>(mstNodes); List<GraphEdge> mstEdges = new List<GraphEdge>(); // Initialize the MST with the start nodes. inMst.Add(startFrom); toAdd.Remove(startFrom); edgesLeadingToNode[startFrom] = new List<GraphEdge>(); foreach (GraphNode otherNode in toAdd) { GraphEdge adjacentEdge = new GraphEdge(startFrom, otherNode); adjacentEdgeQueue.Enqueue(adjacentEdge, distances.GetDistance(adjacentEdge)); edgesLeadingToNode[otherNode].Add(adjacentEdge); } while (toAdd.Count > 0 && adjacentEdgeQueue.Count > 0) { GraphEdge shortestEdge = adjacentEdgeQueue.Dequeue(); mstEdges.Add(shortestEdge); GraphNode newIn = shortestEdge.outside; //if (inMst.Contains(newIn)) throw new Exception(); //if (!toAdd.Contains(newIn)) throw new Exception("No edge to this node should remain!"); inMst.Add(newIn); toAdd.Remove(newIn); // Remove all edges that are entirely inside the MST now. foreach (GraphEdge obsoleteEdge in edgesLeadingToNode[newIn]) { //if (!inMst.Contains(obsoleteEdge.inside)) throw new Exception("This edge's inside node is not inside"); adjacentEdgeQueue.Remove(obsoleteEdge); } edgesLeadingToNode.Remove(newIn); // Find all newly adjacent edges and enqueue them. foreach (GraphNode otherNode in toAdd) { GraphEdge adjacentEdge = new GraphEdge(newIn, otherNode); adjacentEdgeQueue.Enqueue(adjacentEdge, distances.GetDistance(adjacentEdge)); edgesLeadingToNode[otherNode].Add(adjacentEdge); } } if (toAdd.Count > 0) throw new DistanceLookup.GraphNotConnectedException(); this.SpanningEdges = mstEdges; _isSpanned = true; return mstEdges; }