A*算法的理解和使用 【C#版本】

 

项目中使用到了自动寻路这些功能,正好总结一下A*的各种概念和个人的理解

 

 

目录

 

1、A*算法的基本概念

2、A*算法的寻路过程

3、图解

4、代码

1、A*算法的基本概念

启发式搜索:

    启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。

 

搜索区域(The Search Area):

    图中的搜索区域被划分为了简单的二维数组,数组每个元素对应一个小方格,当然我们也可以将区域等分成是五角星,矩形等,通常将一个单位的中心点称之为搜索区域节点(Node)。

 

开放列表(Open List):

     我们将路径规划过程中待检测的节点存放于Open List中,而已检测过的格子则存放于Close List中。

 

关闭列表(Close List):

    我们将路径规划过程中已经检查过的节点放在Close List。

 

父节点(parent):

    在路径规划中用于回溯的节点。

 

启发函数(Heuristics Function)(估价函数):

H为启发函数,也被认为是一种试探,由于在找到唯一路径前,我们不确定在前面会出现什么障碍物,因此用了一种计算H的算法,具体根据实际场景决定。在我们简化的模型中,H采用的是传统的曼哈顿距离(Manhattan Distance)估价函数,也就是横纵向走的距离之和。

H(n) = D * (abs ( n.x – goal.x ) + abs ( n.y – goal.y ) )

 

F(n) = G + H。

F代表当前检查点的总花费,G代表起点到当前检查点的花费,H代表当前检查点到终点的预估花费。

当然,H的函数可以是其他的估价函数, 例如 : 对角线距离和欧几里得距离,比较麻烦 ,可以自己查查

 

2、A*算法的寻路过程

1.将起点A添加到open列表中(A没有计算花费F是因为当前open列表只有这一个节点)。 2.检查open列表,选取花费F最小的节点M(检查M如果为终点是则结束寻路,如果     open列表    没有则寻路失败,直接结束)。 3.对于与M相邻的每一节点N:     1>如果N是阻挡障碍,那么不管它。     2>如果N在closed列表中,那么不管它。     3>如果N不在open列表中:添加它然后计算出它的花费    F(n)=G+H。     4>如果N已在open列表中:当我们使用当前生成的路径时,    检查F花费是否    更小。如果是,更新它的花费F和它的    父节点。 4.重复2,3步。

3、图解

4、代码

(代码是之前网上的 ,但是有错误,修改了一下 ,可以使用)

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Maze
    {
        public const int OBLIQUE = 14;
        public const int STEP = 10;
        public int[,] MazeArray { get; private set; }
        List<Point> CloseList;
        List<Point> OpenList;

        public Maze(int[,] maze)
        {
            this.MazeArray = maze;
            OpenList = new List<Point>(MazeArray.Length);
            CloseList = new List<Point>(MazeArray.Length);
        }

        public Point FindPath(Point start, Point end, bool IsIgnoreCorner)
        {
            OpenList.Add(start);
            int count = 0;
            while (OpenList.Count != 0)
            {
                //找出F值最小的点
                OpenList = OpenList.OrderBy(open => open.F).ToList();
                Point tempStart = OpenList[0];
                OpenList.RemoveAt(0);
                CloseList.Add(tempStart);
                //找出它相邻的点
                var surroundPoints = SurrroundPoints(tempStart, IsIgnoreCorner);
                foreach (Point point in surroundPoints)
                {
                    if (OpenList.Exists(point))
                        //计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并        更新G和F
                        FoundPoint(tempStart, point);
                    else
                        //如果它们不在开始列表里, 就加入, 并设置父节点,并计算GHF
                        NotFoundPoint(tempStart, end, point);
                }
                if (OpenList.Get(end) != null)
                    return OpenList.Get(end);
                if (count>3000) // 循环了3000次跳出 防止卡死
                    return null;
            }
            return OpenList.Get(end);
        }

        private void FoundPoint(Point tempStart, Point point)
        {
            var G = CalcG(tempStart, point);
            if (G < point.G)
            {
                point.ParentPoint = tempStart;
                point.G = G;
                point.CalcF();
            }
        }

        private void NotFoundPoint(Point tempStart, Point end, Point point)
        {
            point.ParentPoint = tempStart;
            point.G = CalcG(tempStart, point);
            point.H = CalcH(end, point);
            point.CalcF();
            OpenList.Add(point);
        }

        private int CalcG(Point start, Point point)
        {
            int G = (Math.Abs(point.X - start.X) + Math.Abs(point.Y - start.Y)) == 2 ? STEP : OBLIQUE;
            int parentG = point.ParentPoint != null ? point.ParentPoint.G : 0;
            return G + parentG;
        }

        private int CalcH(Point end, Point point)
        {
            int step = Math.Abs(point.X - end.X) + Math.Abs(point.Y - end.Y);
            return step * STEP;
        }

        //获取某个点周围可以到达的点
        public List<Point> SurrroundPoints(Point point, bool IsIgnoreCorner)
        {
            var surroundPoints = new List<Point>(9);

            for (int x = point.X - 1; x <= point.X + 1; x++)
                for (int y = point.Y - 1; y <= point.Y + 1; y++)
                {
                    if (CanReach(point, x, y, IsIgnoreCorner))
                        surroundPoints.Add(x, y);
                }
            return surroundPoints;
        }

        //在二维数组对应的位置不为障碍物
        private bool CanReach(int x, int y)
        {
            return MazeArray[x, y] == 0;
        }

        public bool CanReach(Point start, int x, int y, bool IsIgnoreCorner)
        {
            if (!CanReach(x, y) || CloseList.Exists(x, y))
                return false;
            else
            {
                if (Math.Abs(x - start.X) + Math.Abs(y - start.Y) == 1)
                    return true;
                //如果是斜方向移动, 判断是否 "拌脚"
                else
                {
                    if (CanReach(Math.Abs(x - 1), y) && CanReach(x, Math.Abs(y - 1)))
                        return true;
                    else
                        return IsIgnoreCorner;
                }
            }
        }
    }

    //Point 类型
    public class Point
    {
        public Point ParentPoint { get; set; }
        public int F { get; set; }  //F=G+H
        public int G { get; set; }
        public int H { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public Point(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
        public void CalcF()
        {
            this.F = this.G + this.H;
        }
    }

    //对 List<Point> 的一些扩展方法
    public static class ListHelper
    {
        public static bool Exists(this List<Point> points, Point point)
        {
            foreach (Point p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return true;
            return false;
        }

        public static bool Exists(this List<Point> points, int x, int y)
        {
            foreach (Point p in points)
                if ((p.X == x) && (p.Y == y))
                    return true;
            return false;
        }

        public static Point MinPoint(this List<Point> points)
        {
            points = points.OrderBy(p => p.F).ToList();
            return points[0];
        }
        public static void Add(this List<Point> points, int x, int y)
        {
            Point point = new Point(x, y);
            points.Add(point);
        }

        public static Point Get(this List<Point> points, Point point)
        {
            foreach (Point p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return p;
            return null;
        }

        public static void Remove(this List<Point> points, int x, int y)
        {
            foreach (Point point in points)
            {
                if (point.X == x && point.Y == y)
                    points.Remove(point);
            }
        }
    }
}



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[,] array = {
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                           { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1},
                           { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1},
                           { 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1},
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
                           };
            Maze maze = new Maze(array);
            Point start = new Point(1, 1);
            Point end = new Point(7, 7);
            Console.WriteLine(System.DateTime.Now.Ticks / 10000);
            var parent = maze.FindPath(start, end, false);

            List<string> path = new List<string>();
            while (parent != null)
            {
                path.Add(parent.X + "_" + parent.Y);
                parent = parent.ParentPoint;
            }
            Console.WriteLine(System.DateTime.Now.Ticks / 10000);

            path.Reverse();

            for (int i = 0; i < array.GetLength(0); i++)
            {
                for (int j = 0; j < array.GetLength(1); j++)
                {
                    string s = i + "_" + j;
                    if (path.Contains(s))
                    {
                        Console.Write("0*");
                        Console.Write(" ");
                    }
                    else
                    {
                        Console.Write(array[i, j]);
                        Console.Write("  ");
                    }
                }
                Console.WriteLine();
                Console.WriteLine();
            }
        }
    }


 


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值