简体   繁体   中英

What is the best way to route paths in a large grid?

I'm working on an algorithm to find a set of non intersected paths in a grid for a given pairs of points.. Like this for these pairs: (9,4) and (12,13) 样本网格

The output should be something like this:

    9,10,11,7,3,4

    13,14,15,16,12

and print "Blocked" if it can't route all paths

First I searched for an already made algorithm to find all simple paths between 2 points in a graph or a grid. and I found this one by @Casey Watson and @svick here .. It works really well but for small graphs only.

I converted it to C#.NET and enhanced it a little bit to be able to find paths of maximum length X. and build on it my total algorithm.

The one I built works fine in small graphs.. Here is routes 9 pairs in a 8x8 grid.. 在此输入图像描述

but it takes a huge time in larger ones like the 16x16 or even the final one I intended to do which is a 3D model of 16x16x2 Like this

8x8x2网格

The algorithm was developed to be a depth first search RECURSIVE algorithm, but it took a huge time to return value to the user. so I decided to convert it to loops instead of the recursive calls so that I can benefit from yield return feature in .NET but still it didn't help any better.

The loops version of the algorithm find a route for a pair of points in less than a second but the recursive one took more than 90 seconds.

在此输入图像描述

when I tried with 2 pairs, the loops version took around 342 seconds but the recursive one took around 200..

在此输入图像描述

So I can't know which is faster..!? the recursive or the loops one..

I really want to know the best way to do this..

Note : the first digit in the number of the node determine the layer (Starts at 1)..

Here is the code

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;

    namespace AlgorithmTest
    {
     struct Connection
    {
    public int FirstNode;
    public int SecondNode;

    public Connection(int N1,int N2)
    {
        FirstNode = N1;
        SecondNode = N2;
    }
}
enum Algorithm
{ Recursion, Loops }

public class Search
{

    private const int MAX = 15;

    private const int Width = 16;
    private const int Length = 16;
    private const int Height = 2;



    private static void Main(string[] args)
    {


        var graph = new Graph();


        var str = new int[Height,Length, Width];
        var level = ((int)Math.Pow(10, (Length * Width).ToString().Length) >= 100) ? (int)Math.Pow(10, (Length * Width).ToString().Length) : 100;              
        for (var i = 0; i < Height; i++)
        {
            int num = 0;
            for (var j = 0; j < Length; j++)
                for (var k = 0; k < Width; k++)
            {
                str[i, j, k] = ++num + level;

            }
            level += level;
        }


        for (var i = 0; i < Height; i++)
        {
            for (var j = 0; j < Length; j++)
            {
                for (var k = 0; k < Width; k++)
                {

                    if (i < Height - 1) graph.addEdge(str[i, j, k], str[i + 1, j, k]);
                    if (i > 0) graph.addEdge(str[i, j, k], str[i - 1, j, k]);

                    if (k < Width - 1) graph.addEdge(str[i, j, k], str[i, j, k + 1]);
                    if (k > 0) graph.addEdge(str[i, j, k], str[i, j, k - 1]);

                    if (j < Length - 1) graph.addEdge(str[i, j, k], str[i, j + 1, k]);
                    if (j > 0) graph.addEdge(str[i, j, k], str[i, j - 1, k]);


                }
            }
        }



        var wt = new Stopwatch();

       wt.Start();
        var connectedNodes = new List<Connection>()
                                 {



                                     new Connection(1030, 1005),
       //                              new Connection(1002, 1044),
    //                                         new Connection(1015, 1064),
    //                                        new Connection(1041, 1038),
    //                                         new Connection(1009, 1027),
    //                                         new Connection(1025, 1018),
    //                                         new Connection(1037, 1054),
    //                                         new Connection(1049, 1060),
    //                                         new Connection(1008, 1031),
    //                                         new Connection(1001, 1035),

                                 };
        wt.Start();
        Console.WriteLine("Using Loops:");
        Console.WriteLine();
        var allPaths = new Search().FindAllPaths(connectedNodes, graph, MAX, Algorithm.Loops);
        wt.Stop();
        foreach (var path in allPaths)
        {
            PrintPath(path);
        }
        Console.WriteLine("Total Seconds: " + wt.Elapsed.TotalSeconds + ", Number of paths: " + allPaths.Count());
        Console.WriteLine("***************************************************************************************************");
        Console.WriteLine("Using Recursion:");
        Console.WriteLine();
        wt.Reset();
        wt.Start();
        allPaths = new Search().FindAllPaths(connectedNodes, graph, MAX, Algorithm.Recursion);
        wt.Stop();
        foreach (var path in allPaths)
        {
            PrintPath(path);
        }
        Console.WriteLine("Total Seconds: " + wt.Elapsed.TotalSeconds + ", Number of paths: " + allPaths.Count());
        Console.WriteLine();

    }

    private IEnumerable<List<int>> FindAllPaths(List<Connection> connectedNodes, Graph graph, int max, Algorithm algorithm)
    {
        var paths=new Stack<List<int>>();
        var blocked=new List<int>();

        for (var i = 0; i < connectedNodes.Count; i++)
        {
            if (!blocked.Contains(connectedNodes[i].FirstNode)) blocked.Add(connectedNodes[i].FirstNode);
            if (!blocked.Contains(connectedNodes[i].SecondNode)) blocked.Add(connectedNodes[i].SecondNode);
        }

        if (algorithm == Algorithm.Recursion)
        {
            if (FindAllPaths(connectedNodes, 0, max, graph, paths, blocked))
            {
                Console.WriteLine("BLOCKED");
                return new List<List<int>>();
            }
        }
        else if(algorithm==Algorithm.Loops)
        {
            if (!FindAllPaths2(connectedNodes, 0, max, graph, paths, blocked))
            {
                Console.WriteLine("BLOCKED");
                return new List<List<int>>();
            }
        }

        return paths;

    }
    private static bool FindAllPaths(List<Connection> connectedNodes,int order,int max, Graph graph, Stack<List<int>> allPaths, List<int> blocked)
    {

        if (order >= connectedNodes.Count) return false;


        var paths = SearchForPaths(graph, connectedNodes[order].FirstNode, connectedNodes[order].SecondNode, max, blocked);
        if (paths.Count == 0) return true;
        int i;
        for (i = 0; i < paths.Count; i++)
        {
            var path = paths[i];
            allPaths.Push(path);
            blocked.AddRange(path);


            if (!FindAllPaths(connectedNodes, order + 1,max, graph, allPaths, blocked)) break;

            allPaths.Pop();
            foreach (var j in path)
            {
                blocked.RemoveAll(num => num==j);
            }

            paths.RemoveAll(list => IsListsSimilar(list,path));

            i--;

        }
        if (i == paths.Count) return true;


        return false;

    }

    private static bool IsListsSimilar(List<int> L1,List<int> L2)
    {
        if (L2.Count > L1.Count) return false;

        for (int i = 0; i < L2.Count - 1; i++)
        {
            if (L1[i] != L2[i]) return false;
        }
        return true;
    }

    private static List<List<int>> SearchForPaths(Graph graph, int start, int end, int max, List<int> blocked)
    {
        blocked.Remove(start);
        blocked.Remove(end);




        var nodePaths = new List<List<int>>();
        var visited = new LinkedList<int>();
        visited.AddLast(start);
        DepthFirstSearch(graph, visited, end, max, blocked, nodePaths);



        nodePaths = nodePaths.OrderBy(list => list.Count).ToList();

        return nodePaths;

    }
    private static void DepthFirstSearch(Graph graph, LinkedList<int> visited, int end, int max, List<int> blocked, List<List<int>> paths)
    {
        var nodes = graph.adjacentNodes(visited.Last.Value);
        // examine adjacent nodes
        var nodeCount = blocked.Count;
        for (int i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(blocked[i])) return;
        }

        if (visited.Count > max) return;


        nodeCount = nodes.Count;
        for (var i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(nodes[i]) || nodes[i] != end) continue;

            visited.AddLast(nodes[i]);

            {
                paths.Add(new List<int>(visited));

            }
            visited.RemoveLast();
            break;
        }



        nodeCount = nodes.Count;
        for (var i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(nodes[i]) || nodes[i] == end) continue;

            visited.AddLast(nodes[i]);
            DepthFirstSearch(graph, visited, end, max, blocked, paths);
            visited.RemoveLast();
        }

    }

    private static bool FindAllPaths2(List<Connection> connectedNodes, int order, int max, Graph graph, Stack<List<int>> allPaths, List<int> blocked)
    {

        if (order >= connectedNodes.Count) return false;


        foreach (var path in SearchForPaths2(graph, connectedNodes[order].FirstNode, connectedNodes[order].SecondNode, max, blocked))
        {

            allPaths.Push(path);
            blocked.AddRange(path);


            if (!FindAllPaths2(connectedNodes, order + 1, max, graph, allPaths, blocked)) break;

            allPaths.Pop();
            foreach (var j in path)
            {
                blocked.RemoveAll(num => num == j);
            }


        }




        return true;

    }
    private static IEnumerable<List<int>> SearchForPaths2(Graph graph, int start, int end, int max, List<int> blocked)
    {
        blocked.Remove(start);
        blocked.Remove(end);


        var visited = new LinkedList<int>();
        visited.AddLast(start);
        foreach (var VARIABLE in DepthFirstSearch(graph, visited, end, max, blocked))
        {
            yield return VARIABLE;
        }

    }
    private static IEnumerable<List<int>> DepthFirstSearch(Graph graph, LinkedList<int> visited, int end, int max, List<int> blocked)
    {





        var nodes = graph.adjacentNodes(visited.Last.Value);


        var nodeCount = blocked.Count;
        for (int i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(blocked[i])) yield break;
        }


        if (visited.Count > max) yield break;

        nodeCount = nodes.Count;
        for (var i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(nodes[i]) || nodes[i] != end) continue;

            visited.AddLast(nodes[i]);

            yield return (new List<int>(visited));
            visited.RemoveLast();
            break;
        }




        nodeCount = nodes.Count;
        for (var i = 0; i < nodeCount; i++)
        {
            if (visited.Contains(nodes[i]) || nodes[i] == end) continue;

            visited.AddLast(nodes[i]);
            foreach (var P in DepthFirstSearch(graph, visited, end, max, blocked))
            {

                yield return P;

            }

            visited.RemoveLast();

        }






    }


    private static void PrintPath(List<int> visited)
    {

        for (int i = 0; i < visited.Count()-1; i++)
        {
            Console.Write(visited[i]);
            Console.Write(" --> ");
        }
        Console.Write(visited[visited.Count() - 1]);

        Console.WriteLine();
        Console.WriteLine();

    }


}
public class Graph
{
    private readonly Dictionary<int, HashSet<int>> map = new Dictionary<int, HashSet<int>>();

    public void addEdge(int node1, int node2)
    {
        HashSet<int> adjacent = null;

        map.TryGetValue(node1, out adjacent);

        if (adjacent == null)
        {
            adjacent = new HashSet<int>();
            map.Add(node1, adjacent);
        }
        adjacent.Add(node2);
    }

    public List<int> adjacentNodes(int last)
    {
        HashSet<int> adjacent = null;

        map.TryGetValue(last, out adjacent);

        if (adjacent == null)
        {
            return new List<int>();
        }
        return new List<int>(adjacent);
    }
}
    }

I think the answer lies in how you have numbered the nodes in your grid. For a simple 2-dimensional grid, 4 nodes by 4, you would number them : 00, 01, 02, 03, 10, 11, 12 ... 30, 31, 32, 33. Think of them as composite number strings (not decimal values) acting as dimension-based node addresses.

In a 3-dimensional grid, they would be numbered 000, 001, 002, etc. up to 330, 331, 332, 333.

If you want to find all routes between two points 10 and 22 you can quickly calculate their distance by adding the dimensional differences: 1y is one away from 2y, and x0 is two away from x2. Therefore the node distance is 3, you will need to travel over 3 edges (connecting 4 nodes in total) to reach the destination.

The solution space (the only nodes that could ever be involved in a solution route) can be enumerated by creating a set of embedded FOR/NEXT loops, one for each dimension. In this case, the start and end values of 10 and 22 would produce: 10, 11, 12, 20, 21 and 22.

Now comes the clever bit. You can precompute (prepare) a table of 'forwarding' connections between the nodes in your array. Node 10 connects to 20 and 11 (both 1 dimensional difference away). From that you can generate a sequence of valid pathways from 10 to 22 by adding one to a dimension difference in which ever direction you plan to move (in a 2-D array you only get to choose one of two ways. In 3-D you get three choices).

Each answer should be the shortest possible distance. The computation time for this approach should be milliseconds. On a steam powered ZX81! ;o)

I'd like to give diagrams, but there doesn't seem to be a facility to upload them to stackoverflow.

I hope this is helpful to you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM