9

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) Sample Grid

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.. enter image description here

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 grid

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.

enter image description here

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

enter image description here

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);
    }
}
    }
Community
  • 1
  • 1
Islam Mustafa
  • 211
  • 2
  • 11
  • 1
    Did you try other Algorithms like `A*` or `Dijkstra` it should be possible to calculate the shortest path fairly quick with those also see: http://www.codeproject.com/Articles/118015/Fast-A-Star-2D-Implementation-for-C – Jos Vinke Oct 24 '12 at 18:30
  • @Jos, I've never considered that because I do not want to calculate the shortest path, I just want to route **multiple** pair of points in a grid without intersecting. But I'll try to modify Dijkstra algorithm and try again.. – Islam Mustafa Oct 24 '12 at 23:46
  • 1
    Ok thanks, I didn't get it like that the first time i did read it. I think you can do that by 'deleting' the used vertexes from the grid and repeating the shortest path multiple times. But I'm not sure if that's the 'ideal' solution. Good luck with your project anyway. :) – Jos Vinke Oct 25 '12 at 09:29
  • I'll try that, but I do delete check paths and even similar ones. but as you said I think there is an ideal solution to my problem, and I'm waiting for answers.. :) – Islam Mustafa Oct 26 '12 at 06:10
  • I don't understand what is the problem. Are you asking about whether recursion is more efficient than looping or vice versa? Or are you asking for an efficient algorithm for the non-intersecting paths problem? – Mohammad Alaggan Oct 27 '12 at 18:55
  • @M.Alaggan , I'm asking about the best way or algorithm that fit my case described above, provided that it is efficient and can excel in large grids. The part about loops and recursion is just to say that I've tried both and the results wasn't relevant. sometimes loops are faster, and sometimes recursion is faster. – Islam Mustafa Oct 28 '12 at 18:06
  • @IslamMoustafa you want paths of maximum length? – Mohammad Alaggan Oct 28 '12 at 18:59
  • @M.Alaggan, yes the paths must be of maximum length that I specified when I call the function. – Islam Mustafa Oct 31 '12 at 09:35
  • @IslamMoustafa You mean the paths' lengths should not exceed a maximum threshold which you specify as a parameter, right? When you said "of maximum length" I understood that the paths should be as long as possible! – Mohammad Alaggan Nov 01 '12 at 03:53
  • @M.Alaggan Yes right, my paths should not exceed a maximum number which I specify when I call the function. Sorry that I didn't make my self clear in the main post.. – Islam Mustafa Nov 01 '12 at 14:02
  • @IslamMoustafa In general (if the graph was not a grid) then a greedy solution might not work. For instance, suppose you want to choose two paths; path A and path B, and that you start by picking path A as the shortest path then pick the next shortest path B. In that case B may be longer than desired. It may be the case that picking A and B of moderate size would be *the* solution instead. This gives the problem a combinatoric nature and raises suspicion it may be NP-hard. The grid structure may help avoiding this. Do you need exactly *two* paths, or you can ask for $n$ paths at the same time? – Mohammad Alaggan Nov 02 '12 at 07:10

1 Answers1

4

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!

Nimantha
  • 6,405
  • 6
  • 28
  • 69
curzonnassau
  • 138
  • 3
  • You can upload diagrams and pictures on stack , check the editor – xsari3x Oct 29 '12 at 06:38
  • @curzonnassau, Yes I agree with, I think the proposed solution could solve my problem. But can you provide any code or diagrams. – Islam Mustafa Oct 31 '12 at 09:39
  • 1
    @IslamMoustafa and xsari3x, if you Google some images using "node routing hypercube" you should get plenty of good examples. Some use binary node addressing, but that's only because they have small hypercubes. – curzonnassau Nov 02 '12 at 20:05