Astar算法循环不断

本文关键字:循环 算法 Astar | 更新日期: 2023-09-27 18:27:24

我在研究A*搜索算法,我遵循了这个算法:A*搜索方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MapData.Interfaces;
namespace MapData.Algorithm {
    public class AStar {
        IDMap map;
        List<Location2D> openset;
        List<Location2D> closedset;
        List<Location2D> path;
        Dictionary<Location2D, Double> g_scores;
        Dictionary<Location2D, Double> h_scores;
        Dictionary<Location2D, Double> f_scores;
        Dictionary<Location2D, Location2D> came_from;
        int[] ArrayX = { 0, -1, -1, -1, 0, 1, 1, 1 };
        int[] ArrayY = { 1, 1, 0, -1, -1, -1, 0, 1 };
        /// <summary>
        /// Initing the Algorithm with MapInfo
        /// </summary>
        /// <param name="map">DMap info.</param>
        public AStar(IDMap map) {
            this.map = map;
        }
        /// <summary>
        /// Start searching untile it's find it's goal or return false.
        /// </summary>
        /// <param name="start">Starting Node.</param>
        /// <param name="goal">Ending Node.</param>
        public bool Find( Location2D start, Location2D goal) {
            openset = new List<Location2D>(); // The set of tentative nodes to be evaluated, initially containing the start node.
            closedset = new List<Location2D>(); // The set of nodes already evaluated.
            path = new List<Location2D>(); // The path result.
            g_scores = new Dictionary<Location2D, Double>(); // Cost from start along best known path.
            h_scores = new Dictionary<Location2D, Double>(); // Estimated cost to get from a node to the goal.
            f_scores = new Dictionary<Location2D, Double>(); // Estimated total cost from start to goal.
            came_from = new Dictionary<Location2D, Location2D>(); // The navigated nodes.

            openset.Add(start);
            g_scores[start] = 0;
            h_scores[start] = GetHValue(start, goal);
            f_scores[start] = g_scores[start] + h_scores[start];
            while (openset.Count > 0) { // while openset is not empty
                Location2D current = CheckBestNode(); //the node in openset having the lowest f_score[] value

                if (current.Equals(goal)) {
                    ReconstructPathRecursive(current);
                    return true;
                }
                Location2D neighbor = new Location2D();
                for (int i = 0; i < 8; i++) { // neighbor nodes.
                    neighbor.X = (ushort)(current.X + ArrayX[i]);
                    neighbor.Y = (ushort)(current.Y + ArrayY[i]);
                    bool tentative_is_better = false;
                    if (closedset.Contains(neighbor)) continue;
                    if (!map.Cells[neighbor.X, neighbor.Y].Walkable) continue;
                    Double tentative_g_score = g_scores[current] + DistanceBetween(current, neighbor);
                    if (!openset.Contains(neighbor)) {
                        openset.Add(neighbor);
                        h_scores[neighbor] = GetHValue(neighbor, goal);
                        tentative_is_better = true;
                    } else if (tentative_g_score < g_scores[neighbor]) {
                        tentative_is_better = true;
                    } else {
                        tentative_is_better = false;
                    }
                    if (tentative_is_better) {
                        if (came_from.ContainsKey(neighbor)) {
                            came_from[neighbor] = current;
                        } else {
                            came_from[neighbor] = current;
                            g_scores[neighbor] = tentative_g_score;
                            f_scores[neighbor] = g_scores[neighbor] + h_scores[neighbor];
                        }
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Check the best node that has the smallest f value;
        /// </summary>
        /// <returns>The best Node.</returns>
        Location2D CheckBestNode() {
            var BestNode = f_scores.OrderBy(x => x.Value).First().Key;
            f_scores.Remove(BestNode);
            openset.Remove(BestNode);
            closedset.Add(BestNode);
            return BestNode;
        }
        private void ReconstructPathRecursive(Location2D current_node) {
            Location2D temp;
            if (came_from.TryGetValue(current_node, out temp)) {
                ReconstructPathRecursive(temp);
                path.Add(temp);
            } else {
                path.Add(current_node);
            }
        }
        int GetHValue(Location2D start, Location2D goal) {
            int nDx = Math.Abs(start.X - goal.X);
            int nDy = Math.Abs(start.Y - goal.Y);
            if (nDx > nDy)
                return 10 * nDx + 6 * nDy;
            return 10 * nDy + 6 * nDx;
        }
        readonly Double SQRT_2 = Math.Sqrt(2);
        protected  Double DistanceBetween(Location2D inStart, Location2D inEnd) {
            int diffX = Math.Abs(inStart.X - inEnd.X);
            int diffY = Math.Abs(inStart.Y - inEnd.Y);
            switch (diffX + diffY) {
                case 1: return 1;
                case 2: return SQRT_2;
                case 0: return 0;
                default:
                    throw new ApplicationException();
            }
        }
        public List<Location2D> Path {
            get {
                return path;
            }
        }
    }
}

现在我有两个问题:

  1. 如果我在可步行的区域进行检查,阿斯塔会无休止地循环,永远找不到路径,也不会中断。

  2. 如果我评论了正在检查可步行区域的部分,它会找到路径,但不准确,比如如果目标位置是->(444444),则路径结束于->(444443)或(443444)。

我看不出哪里出了问题,因为我遵循了维基百科的指南。

Astar算法循环不断

现在,如果我错了,请纠正我,但似乎,从查看其他代码来看,您希望在当前节点的邻居上迭代的for循环中的if语句看起来像这样:

if (!closedset.Contains(neighbor)) continue;
if (map.Cells[neighbor.X, neighbor.Y].Walkable) continue;

不是这样的:

if (closedset.Contains(neighbor)) continue;
if (!map.Cells[neighbor.X, neighbor.Y].Walkable) continue;

您的原始代码只在邻居在封闭列表上并且不可遍历时才对其求值,我认为您不希望这样做。更新后的代码仅在相邻节点不在闭集上且可行走时才计算该节点。

要进一步了解A*算法并详细描述其实际工作方式,请查看此链接。

编辑:你说你的代码提供了一个不准确的路径,也就是说,一个没有一直通向目标广场的路径。从您的ReconstructPathRecursive()函数来看,您似乎犯了一个重大错误。因为您的列表包含遍历的每个节点的父节点,所以您最终会得到除目标正方形之外的所有节点,因为它不是任何节点的父对象。目标正方形应该是在开始从当前节点的父节点(即目标正方形)反向重建路径之前添加到列表中的第一个正方形。

为了修复这种行为,我建议当您发现当前节点等于目标节点时,首先将目标节点添加到路径列表中,然后调用ReconstructPathRecursive()函数来获得最终列表。