3

I want to find the minimum weighted path between two indexes of an 2D array. I have tried to implement Dijkstra's shortest path algorithm and A* but I couldn't. There is an example below. I want to give starting and ending point of path and the path should be returned by the algorithm.

0 2 5 8 8 9

5 1 2 7 9 8

9 8 2 5 7 8

8 8 2 2 2 9

9 7 6 3 2 7

8 8 6 5 3 5

Can anybody reccomend other algorithms or guide me about the algorithms?

I am working on this for days and I don't think it is a challenging problem at all. But I lost my temper and cannot think healty. For example, I even could not understand whether a* and dijkstra's shorthest path is directly related to what I want to do. Or they work for 0 and 1 like structures that if there is a wall you cannot pass from there if not you can. In my problem you can pass from anywhere but I want to find the least cost path etc.

Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67
user1125953
  • 585
  • 2
  • 7
  • 18
  • 3
    what does it mean that you "couldn't" implement dijkstras? Not applicable or just to complex? - Please describe what you tried so far instead of just *demand* the solution to your problem – Random Dev Mar 16 '12 at 10:08
  • @CarstenKönig There is vertices and edges in dijkstras. But here they are the same thing actually. Am I wrong? I couldn't decide on such issues. A code sample would be great to understand but I don't demand the solution directly and want to copy it. I want to know how would people do this? Any help will be appreciated – user1125953 Mar 16 '12 at 11:25
  • Is this helpful?http://stackoverflow.com/questions/9547295/depth-first-search-2d-game-map/9547524#9547524, you can model your environment by directed graph. – Saeed Amiri Mar 16 '12 at 11:27
  • 1
    @SaeedAmiri Isn't it hard to model a 2D array into a directed graph? It is an example but my actual 2D arrays will be around min 300x300 – user1125953 Mar 16 '12 at 11:42
  • @user1125953, if the src and dst index are the same, is the weight = 0, or is the weight equal to the value of the index? The reason I ask is because if the weight is not zero, then that would mean 0,0 -> 4,4 is not the same as 4,4 -> 0,0 – Tung Mar 16 '12 at 13:20
  • @Tung it is 0. So 0,0 to 4,4 = 4,4 to 0,0 – user1125953 Mar 16 '12 at 13:29
  • @user1125953 in your example (0,0) is 0, so the summation is 0+2+1+2+2+2+2+2+2 = 15. If the value at (0,0) was instead 1, then are saying that the summation would be 16? If that's the case, then 0,0 to itself is not 0, but the value of the index, isn't it? – Tung Mar 16 '12 at 13:38
  • @Tung Actually there won't be such a case that start and end will be same. What I basically want is, starting from an index, summing the values that I passed and when I came to end point the total sum is the minnimum possible. – user1125953 Mar 16 '12 at 14:04
  • @user1125953, the hypothetical scenario is to help me understand whether the value at the starting index count towards the summation. – Tung Mar 16 '12 at 14:10
  • @user1125953, your graph is small,900 nodes is nothing, you can model it by directed graph (you should add directed property to what I referenced before) and then because your edges is O(Numder of Nodes) you can run BFS on it. finally you can find the path. for how to find path by BFS you can add Previous property to your graph nodes. – Saeed Amiri Mar 16 '12 at 14:17

3 Answers3

3

Model your problem to "fit" the design of the algorithms:
Try to first build a graph out of your grid, it might help you to understand these algorithms and implement them. Since both these algorithms are designed for graphs [which can model a grid], I think you might find it easier to understand the algorithms when you implement them on "general graphs", and build a graph for your specific problem.

Your graph will be G = (V,E), where V = { (i,j) | (i,j) is a possible index) } [all squares in the grid], and E = { (v,u) | v and u are adjacent vertices on the grid }.
You also need a weighting function on edges. w:E->N. it will be w(u,v) = matrix[v.x][v.y] [the value of the matrix in the entree matching to v].

Now, implement dijkstra in your facorite language, for the graph G. The weight of your shortest path is the weight of the path found by dijkstra + matrix[source.x][source.y] [because we did not add this value to any edge on the shortest path].

To find the actual path, and not only the weight of it - you will also need to hold a map:V->V, which will map from each vertex - to the vertex which discovered it. Similar to the idea explained in this post.

Start with the simpler Dijkstram and later move on to A*:
I'd start with dijkstra and not A*, since A* is basically an informed dijkstra - so you should be able to implement dijkstra before you implement A*, since it [dijkstra] is simpler.

Other algorithms for shortest path:
You should also know, that there is also another common shortest path algorithm - the well-known Bellman-Ford [which can handle negative weights as well, unlike dijkstra].

Community
  • 1
  • 1
amit
  • 175,853
  • 27
  • 231
  • 333
  • Thank you. I will implement it in c#. I don't know how to build a graph in c# so i am searching about it now, thank you. I hope these will be helpful – user1125953 Mar 16 '12 at 13:05
  • @user1125953: You might want to read about possible [representations for graph](http://en.wikipedia.org/wiki/Graph_%28data_structure%29#Representations) as well – amit Mar 16 '12 at 13:48
1

Here's a sample I whipped up that seems to work. To be more efficient, you need to implement a min heap when searching for the next shortest distance node.

private static int FindMin(int[,] indexWeights, Tuple<int, int> src, Tuple<int, int> dst)
{
    List<Node> allNodes = new List<Node>(indexWeights.GetLength(0)*indexWeights.GetLength(1));
    Node[,] graph = GenerateGraph(indexWeights, allNodes);

    Queue<Node> queue = new Queue<Node>();
    Node currentNode = graph[src.Item1, src.Item2];

    // 0 ? or the weight value at the index? This was not too clear from your example
    // Setting the starting distance to 0 means that a->b != b->a because the starting value
    // at index b is not the same as the starting value at index a
    currentNode.Distance = indexWeights[src.Item1, src.Item2];

    queue.Enqueue(currentNode);
    while (queue.Count > 0)
    {
        currentNode = queue.Dequeue();
        currentNode.Visited = true;

        if (currentNode.XCoord == dst.Item1 && currentNode.YCoord == dst.Item2)
            break;

        // Calculate tentative distances
        foreach (Node neighbor in currentNode.Neighbors)
        {
            neighbor.Distance = Math.Min(neighbor.Distance,
                                         currentNode.Distance + indexWeights[neighbor.XCoord, neighbor.YCoord]);
        }

        // Find the node with the minimum distance that hasn't been visited, and visit that next. 
        // A min-heap would be BEST for getting the next node, but I'll leave that as an exercise for you
        Node nonVisitedMinNode = allNodes.Where(a => !a.Visited)
            .Aggregate((currMin, currNode) => currMin.Distance < currNode.Distance ? currMin : currNode);

        queue.Enqueue(nonVisitedMinNode);
    }

    return graph[dst.Item1, dst.Item2].Distance;
}

public class Node
{
    public Node(int xCoord, int yCoord)
    {
        XCoord = xCoord;
        YCoord = yCoord;

        Distance = int.MaxValue;
        Visited = false;
        Neighbors = new List<Node>();
    }

    public int XCoord { get; set; }
    public int YCoord { get; set; }
    public int Distance { get; set; }
    public bool Visited { get; set; }
    public List<Node> Neighbors { get; set; }
}

public static Node[,] GenerateGraph(int[,] weight, List<Node> allNodes)
{
    Node[,] nodes = new Node[weight.GetLength(0),weight.GetLength(1)];
    for (int i = 0; i < weight.GetLength(0); i++)
    {
        for (int j = 0; j < weight.GetLength(1); j++)
        {
            nodes[i, j] = new Node(i, j);
            allNodes.Add(nodes[i, j]);
        }
    }

    // Couldn't think of a way to combine the two loops together to set neighbors
    for (int i = 0; i < weight.GetLength(0); i++)
    {
        for (int j = 0; j < weight.GetLength(1); j++)
        {
            if (0 <= (i - 1))
                nodes[i, j].Neighbors.Add(nodes[i - 1, j]);

            if (weight.GetLength(0) > (i + 1))
                nodes[i, j].Neighbors.Add(nodes[i + 1, j]);

            if (0 <= (j - 1))
                nodes[i, j].Neighbors.Add(nodes[i, j - 1]);

            if (weight.GetLength(1) > (j + 1))
                nodes[i, j].Neighbors.Add(nodes[i, j + 1]);
        }
    }

    return nodes;
}

I couldn't think of a non clunky way to generate the graph... maybe it's too late here. Anyway, you may need to tweak the initialization of currentNode.Distance based on what we discussed in the comments. Is [0,0] 0 in your example because it is the starting index, or is it because the value is 0 to begin with? If you give another example where the starting index does not have a value of 0, then it would be easier to understand the rules.

Tung
  • 5,334
  • 1
  • 34
  • 41
  • I am trying your code now. It has been 5 mins and still running it is a big problem:) Actually what I really interested is not the distance, I want to find the path. So I will need to modify your code accordingly. But thank you anyway, I will try to benefit from your code – user1125953 Mar 16 '12 at 14:22
  • @user1125953. The min heap is one of the biggest improvement you could add to the code. I just went with a linear search using linq (which means I search through 900 nodes for the smallest non visited node). At the end of the algorithm, you can backtrack. Since visited nodes have their distance calculated, and you have all the neighbors within `Node`, you can find the path back by following the neighbors with the smallest distance starting from your destination node – Tung Mar 16 '12 at 14:25
  • The starting node will be definitely in the path so weight of it is not important I think. The result I want is : 0,0-...-4,4 – user1125953 Mar 16 '12 at 14:30
0

What you are looking for is the shortest path between 2 points in a 2D-array so, either of Dijkstra or A* would work well for you. What you mentioned with 1s and 0s is nothing but a path finding problem in 2D-arrays, which is more specific case of above algorithms and can be implemented using simple BFS.

As for the implementation, as others mentioned, you need to decide on how you would model your solution such that it suits the design of algorithm you are using. One of the two possible ways I could think are:

  • Convert your 2D array into a graph and run your algorithm between Source and Destination nodes.
  • Represent each cell in a way so that you can run your algorithm without having to convert it into a graph.

Considering you go with Dijkstra, a sample implementation of this problem would be as below:

//Controls the size of your 2D array. Made static for simplicity. Can be allocated dynamically.
#define MAX_NODES 10

/* Your 2D Point Structure. Stores information of each cell of your 2D array */
typedef struct Point
{
    int x, y;    //x and y co-ordinate of your point

    int cost;    //Cell's actual cost

    int cost_from_src;     //Cell's cost from the Source node. This gets updated when algorithm runs

    unsigned int visited;    //Keeps track of nodes that have been popped out of the Queue

    struct Point *par;     //Keeps track of Parent Node

}Point_t, *Point_p;

/* 2D array of Point structure */
Point_t adjMArr[MAX_NODES][MAX_NODES];

/* Finds SP in Weighted 2D-Matrix */
Point_p SPDijkstra(Point_t src, Point_t dest)
{
    Queue_p q = NULL; // You can use your own implementation of a Queue

    // Source is initially put into the Queue
    adjMArr[src.x][src.y].cost_from_src = 0;
    adjMArr[src.x][src.y].par = NULL;
    q = push(q, adjMArr[src.x][src.y]);

    while (!isEmpty(q))
    {
        Point_t pt = extractMin(q); // Get the point with minimum value of "cost_from_src" from the Queue
        int x = pt.x, y = pt.y, new_cost, i;
        adjMArr[x][y].visited = 1;
        q = deleteQ(q, pt); // Delete this point from the Queue and mark it as visited. This point will not be visited again

        if (dest.x == x && dest.y == y)
            return &adjMArr[x][y]; // Destination Point

        /*Check for all the neighbours of Point(x,y) and update the costs of its neighbours add them to the Queue*/
        // Horizontal Left
        if ((x - 1 >= 0) && y < MAX_NODES && !adjMArr[x - 1][y].visited)
        {
            new_cost = adjMArr[x][y].cost_from_src + adjMArr[x - 1][y].cost;
            if (new_cost < adjMArr[x - 1][y].cost_from_src)
            {
                adjMArr[x - 1][y].cost_from_src = new_cost;

                /* To keep track of parent so that once you reach the destination node, you can traverse all the way back to parent */
                adjMArr[x - 1][y].par = &adjMArr[x][y];

                q = push(q, adjMArr[x - 1][y]);
            }
        }
        // Horizontal Right
        if ((x + 1 < MAX_NODES) && y < MAX_NODES && !adjMArr[x + 1][y].visited)
        {
            new_cost = adjMArr[x][y].cost_from_src + adjMArr[x + 1][y].cost;
            if (new_cost < adjMArr[x + 1][y].cost_from_src)
            {
                adjMArr[x + 1][y].cost_from_src = new_cost;
                adjMArr[x + 1][y].par = &adjMArr[x][y];
                q = push(q, adjMArr[x + 1][y]);
            }
        }
        // Vertical Up
        if ((y - 1 >= 0) && x < MAX_NODES && !adjMArr[x][y - 1].visited)
        {
            new_cost = adjMArr[x][y].cost_from_src + adjMArr[x][y - 1].cost;
            if (new_cost < adjMArr[x][y - 1].cost_from_src)
            {
                adjMArr[x][y - 1].cost_from_src = new_cost;
                adjMArr[x][y - 1].par = &adjMArr[x][y];
                q = push(q, adjMArr[x][y - 1]);
            }
        }
        // Vertical Down
        if ((y + 1 < MAX_NODES) && x < MAX_NODES && !adjMArr[x][y + 1].visited)
        {
            new_cost = adjMArr[x][y].cost_from_src + adjMArr[x][y + 1].cost;
            if (new_cost < adjMArr[x][y + 1].cost_from_src)
            {
                adjMArr[x][y + 1].cost_from_src = new_cost;
                adjMArr[x][y + 1].par = &adjMArr[x][y];
                q = push(q, adjMArr[x][y + 1]);
            }
        }
    }
    return NULL; // No path exists
}
Cheshar
  • 571
  • 1
  • 7
  • 21