我想找到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类似的结构,如果有墙,你就无法从那里传递。在我的问题中你可以从任何地方传递,但我想找到最低成本路径等。
答案 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实现。
至于实施,正如其他人所提到的,您需要决定如何为解决方案建模,使其适合您正在使用的算法设计。我能想到的两种可能方式之一是:
考虑到你选择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
}