Нахождение двухмерного массива - PullRequest
3 голосов
/ 16 марта 2012

Я хочу найти минимальный взвешенный путь между двумя индексами двумерного массива.Я пытался реализовать алгоритм кратчайшего пути Дейкстры и 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 53 5

Кто-нибудь может порекомендовать другие алгоритмы или подсказать мне алгоритмы?

Я работаю над этим несколько дней и не думаю, что это вообще сложная проблема.Но я вышел из себя и не могу думать о здоровье.Например, я даже не мог понять, связан ли кратчайший путь * и Дейкстры с тем, что я хочу сделать.Или они работают как 0 и 1 как структуры, которые, если есть стена, вы не можете пройти оттуда, если не можете.В моей задаче вы можете пройти откуда угодно, но я хочу найти путь с наименьшей стоимостью и т. Д.

Ответы [ 3 ]

3 голосов
/ 16 марта 2012

Смоделируйте вашу проблему, чтобы "соответствовать" дизайну алгоритмов:
Попробуйте сначала построить график из вашей сетки, это может помочь вам понять эти алгоритмыи реализовать их.Поскольку оба эти алгоритма предназначены для графов [которые могут моделировать сетку], я думаю , вам может быть проще понять алгоритмы, когда вы реализуете их на «общих графах» и строите граф для ваших конкретныхПроблема.

Ваш график будет 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].

Теперь, внедрите dijkstra на вашем факультативном языке для графа G.Вес вашего кратчайшего пути равен весу пути, найденного dijkstra + matrix[source.x][source.y] [потому что мы не добавляли это значение ни к одному ребру на кратчайшем пути].

Чтобы найти фактический путь, а не только его вес - вам также нужно будет держать map:V->V, который будет отображаться из каждой вершины - в вершину, которая ее обнаружила.Подобно идее, объясненной в этом посте .

Начните с более простого Dijkstram, а затем перейдите к A *:
Я бы начал с dijkstra ине A *, так как A * в основном информированный dijkstra - поэтому вы должны иметь возможность реализовать dijkstra до того, как внедрите A *, так как он [dijkstra] проще.

Другие алгоритмы для кратчайшего пути:
Вы также должны знать, что есть и другой распространенный алгоритм кратчайшего пути - хорошо известный Bellman-Ford [который может обрабатывать и отрицательные веса, в отличие от dijkstra].

1 голос
/ 16 марта 2012

Вот образец, который я взбил, который, кажется, работает. Чтобы быть более эффективным, вам нужно реализовать минимальную кучу при поиске следующего кратчайшего расстояния.

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 голосов
/ 05 сентября 2015

То, что вы ищете, это кратчайший путь между двумя точками в двумерном массиве, так что либо Дейкстра, либо А * будет работать для вас.То, что вы упомянули с 1 и 0, является не чем иным, как проблемой поиска пути в 2D-массивах, которая является более конкретным случаем вышеупомянутых алгоритмов и может быть реализована с использованием простой 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
}
...