二维阵列路径查找

时间:2012-03-16 10:01:35

标签: algorithm multidimensional-array path-finding dijkstra a-star

我想找到2D数组的两个索引之间的最小加权路径。我试图实现Dijkstra的最短路径算法和A *,但我不能。下面有一个例子。我想给出路径的起点和终点,并且路径应该由算法返回。

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

有人可以推荐其他算法或指导我的算法吗?

我正在研究这几天,我认为这根本不是一个具有挑战性的问题。但我发脾气,不能认为是健康。例如,我甚至无法理解a *和dijkstra的最短路径是否与我想要做的事情直接相关。或者它们适用于0和1类似的结构,如果有墙,你就无法从那里传递。在我的问题中你可以从任何地方传递,但我想找到最低成本路径等。

3 个答案:

答案 0 :(得分:3)

对您的问题进行建模以“适应”算法的设计:
尝试首先从网格中构建graph,它可以帮助您理解这些算法并实现它们。由于这两种算法都是针对图形设计的[可以为网格建模],我认为您可能会发现在“常规图形”上实现算法时更容易理解算法,并为您构建图形具体问题。

您的图表将为G = (V,E),其中V = { (i,j) | (i,j) is a possible index) } [网格中的所有正方形]和E = { (v,u) | v and u are adjacent vertices on the grid }
您还需要一个加权函数在边缘w:E->N。它将是w(u,v) = matrix[v.x][v.y] [与v匹配的主菜中矩阵的值。

现在,使用您的facorite语言为图G实施dijkstra。最短路径的权重是dijkstra + matrix[source.x][source.y]找到的路径的权重[因为我们没有将此值添加到最短路径上的任何边缘]。

要查找实际路径,而不仅仅是它的权重 - 您还需要保存一个map:V->V,它将从每个顶点映射到发现它的顶点。与this post中解释的想法相似。

从更简单的Dijkstram开始,然后转到A *: 我从dijkstra而不是A *开始,因为A *基本上是一个知情的dijkstra - 所以你应该能够在实现A *之前实现dijkstra,因为它[dijkstra]更简单。

最短路径的其他算法:
你还应该知道,还有另一种常见的最短路径算法 - 众所周知的Bellman-Ford [也可以处理负权重,与dijkstra不同]。

答案 1 :(得分:1)

这是我掀起的一个似乎有效的样本。为了提高效率,您需要在搜索下一个最短距离节点时实现最小堆。

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;
}

我想不出一种非笨重的方式来生成图表......也许这已经太晚了。无论如何,您可能需要根据我们在评论中讨论的内容调整currentNode.Distance的初始化。在您的示例中是[0,0] 0,因为它是起始索引,还是因为值为0开始?如果您给另一个示例,其中起始索引的值为0,那么理解规则会更容易。

答案 2 :(得分:0)

您正在寻找的是2D阵列中2点之间的最短路径,因此Dijkstra或A *中的任何一个都适合您。您在1和0中提到的只不过是二维数组中的路径查找问题,这是上述算法的更具体情况,可以使用简单的BFS实现。

至于实施,正如其他人所提到的,您需要决定如何为解决方案建模,使其适合您正在使用的算法设计。我能想到的两种可能方式之一是:

  • 将2D数组转换为图形,并在源节点和目标节点之间运行算法。
  • 以某种方式表示每个单元格,以便您可以运行算法而无需将其转换为图形。

考虑到你选择Dijkstra,这个问题的示例实现如下:

//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
}