我创建了IDA *的通用版本来解决寻路问题。我使用了罗马尼亚的路线图,但遇到了一些问题。
我使用的罗马尼亚地图:https://dzone.com/storage/temp/11185033-romanianmap-astar.jpg
例如,对于通往Arad到布加勒斯特的道路,最终结果是Timisoara-Lugoj-Mehadia-Drobeta-Craiova-RâmnicuVâlcea-Sibiu-Făgăraș-Bucharest。同样在布加勒斯特至阿拉德的道路上,最终结果是皮特蒂-拉姆尼库·瓦尔恰-西比乌-阿拉德(正确的结果)。在同一条道路上获得不同的结果是否正常?有一种方法可以优化算法以更快地显示结果?
最初,我开始实现受Leniel Maccaferri启发的A *:https://github.com/leniel/AStar,并将类用于IDA *。
对于启发式算法,我使用了曼哈顿距离和Haversine公式进行了以公里为单位的估算。
这是我尝试过的:
public async Task<List<string>> ResolveIDAStarAlgorithm(string startCity, string destinationCity)
{
/* Creating the graph. */
Graph graph = new Graph();
await CreateGraph(graph);
/* Store the start Node */
Node start = graph.Nodes[startCity];
/* Store the destination Node */
Node destination = graph.Nodes[destinationCity];
/* Sets the cost of the start node. */
start.GCost = 0;
/* Sets the heuristic cost. */
start.HCost = Haversine.Distance(start, destination);
/* Sets the f cost limit. */
double fCostLimit = start.GCost + start.HCost;
List<string> path = new List<string>();
while (true)
{
/* Start IDA Star pathfinding search. */
Node foundTemp = await SearchPath(start, destination, start.GCost, fCostLimit);
switch (foundTemp.Status)
{
/* A new cost limit has been found. */
case SEARCHRETURN.BOUND:
fCostLimit = foundTemp.HCost;
break;
/* The path was found. */
case SEARCHRETURN.FOUND:
path = RetracePath(start, destination);
return path;
/* No path has been found. */
case SEARCHRETURN.NOT_FOUND:
return null;
}
}
}
public async Task<Node> SearchPath(Node currentNode, Node targetNode, double gCost, double fCostLimit)
{
Node retNode = new Node();
Path<Node> path = new Path<Node>(currentNode);
double distance(Node node1, Node node2) =>
node1.Neighbors.Cast<EdgeToNeighbor>().Single(
etn => etn.Neighbor.Key == node2.Key).Cost;
/* If the current node is the target, the search process will finish. */
if (currentNode == targetNode)
{
retNode.Status = SEARCHRETURN.FOUND;
return retNode;
}
double newFCostLimit = gCost + Haversine.Distance(currentNode, targetNode);
if (newFCostLimit > fCostLimit)
{
retNode.Status = SEARCHRETURN.BOUND;
retNode.HCost = newFCostLimit;
return retNode;
}
if (!_visitedNodes.Contains(currentNode.Key))
{
_visitedNodes.Add(currentNode.Key);
}
foreach (Node neighbor in path.LastStep.Neighbours)
{
if (_visitedNodes.Contains(neighbor.Key))
{
continue;
}
if (!_visitedNodes.Contains(neighbor.Key))
{
double newCostNeighbor = distance(currentNode, neighbor) + Haversine.Distance(currentNode, neighbor);
if (newCostNeighbor < neighbor.GCost || !_visitedNodes.Contains(neighbor.Key))
{
neighbor.GCost = gCost + newCostNeighbor;
neighbor.HCost = Haversine.Distance(neighbor, targetNode);
neighbor.PathParent = currentNode;
}
Node t = await SearchPath(neighbor, targetNode, newCostNeighbor, fCostLimit);
switch (t.Status)
{
case SEARCHRETURN.BOUND:
if (t.HCost < _costLimit)
{
_costLimit = t.HCost;
}
break;
case SEARCHRETURN.FOUND:
return t;
case SEARCHRETURN.NOT_FOUND:
continue;
}
}
}
if (_costLimit == _minValue)
{
retNode.Status = SEARCHRETURN.NOT_FOUND;
}
else
{
retNode.HCost = _costLimit;
retNode.Status = SEARCHRETURN.BOUND;
}
_visitedNodes.Remove(currentNode.Key);
return retNode;
}
public List<string> RetracePath(Node start, Node destination)
{
List<string> path = new List<string>();
Node currentNode = destination;
double totalCost = 0;
double distance(Node node1, Node node2) =>
node1.Neighbors.Cast<EdgeToNeighbor>().Single(
etn => etn.Neighbor.Key == node2.Key).Cost;
while (currentNode != start)
{
path.Add(currentNode.Key);
totalCost += distance(currentNode, currentNode.PathParent);
currentNode = currentNode.PathParent;
}
path.Add((totalCost * 0.99).ToString());
path.Reverse();
return path;
}