/// <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;
        }
Exemple #7
0
 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;
        }