A*算法实验

一、实验目的:

熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解N数码难题,理解求解流程和搜索顺序。

二、实验原理:

A*算法是一种有序搜索算法,其特点在于对估价函数的定义上。对于一般的有序搜索,总是选择f值最小的节点作为扩展节点。因此,f是根据需要找到一条最小代价路径的观点来估算节点的,所以,可考虑每个节点n的估价函数值为两个分量:从起始节点到节点n的代价以及从节点n到达目标节点的代价。

三、我的报告

理解
我对A*算法的理解,采用回溯法加贪心算法的方式去搜索图中两个点的最短路径。可以在绕过障碍物的情况,求最短路径。
借用几个图:

贪心的依据:
f = g + h ;
g;当前点到原点的距离,正方形上下左右距离是1,左上角这1.4(根号2)
h:当前点到目标点的距离,求法是:终点与当前点的横坐标差+终点与当前点的纵坐标差。
回溯:
遇到障碍物我们就不计算,但是如果发现周围节点的路径我走过的,那我就要忽略掉他。

代码实现

//三数据结构
package ai.a;

import java.util.Objects;

/**
 * @Author: WYF
 * @Description: 坐标,实现了比较方法
 * @Create: 2020/4/15 10:55
 * @Version: 1.0
 */
public class Coord {
    public int x;
    public int y;

    public Coord(int x, int y) {
        this.x = x;
        this.y = y;
    }

    /**
     * @Description: 重写equel和hashCode方法,可以实现比较
    */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Coord coord = (Coord) o;
        return x == coord.x &&
                y == coord.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

package ai.a;

/**
* @Author: WYF
* @Description: 封装更完整的节点,包括本身坐标数据,G和H和父节点。
* @Create: 2020/4/15 10:59
* @Version: 1.0
*/
public class Node implements Comparable<Node> {
    /** 坐标*/
    public Coord coord;
    /** 父结点*/
    public Node parent;
    /** G:是个准确的值,是起点到当前结点的代价*/
    public int G;
    /** H:是个估值,当前结点到目的结点的估计代价*/
    public int H;

    public Node(int x,int y) {
        this.coord = new Coord(x,y);
    }

    public Node(Coord coord, Node parent, int g, int h) {
        this.coord = coord;
        this.parent = parent;
        G = g;
        H = h;
    }

    /**
     * @Description: 比较的是G+H,H是曼哈顿距离,G是节点本身到起始位置的距离
     * @Param: [node]
     * @Return: int
     * @Author: WYF
     * @Date: 2020/4/15 11:06
    */
    @Override
    public int compareTo(Node node) {
        if (node == null) {
            return -1;
        }
        if (G+H > node.G+node.H) {
            return 1;
        }else if (G+H < node.G+node.H) {
            return -1;
        }
        return 0;
    }
}
package ai.a;

/**
* @Author: WYF
* @Description: A星算法输入的所有数据,封装在一起,传参方便。
* @Create: 2020/4/15 11:09
* @Version: 1.0
*/
public class MapInfo {
    public int[][] maps;
    /** 地图宽度 */
    public int width;
    /** 地图高度 */
    public int hight;
    public Node start;
    public Node end;

    public MapInfo(int[][] maps, int width, int hight, Node start, Node end) {
        this.maps = maps;
        this.width = width;
        this.hight = hight;
        this.start = start;
        this.end = end;
    }
}

//2测试类:
package ai.a;

/**
* @Author: WYF
* @Description: 测试类
* @Create: 2020/4/15 11:13
* @Version: 1.0
*/
public class Test {
    public static void main(String[] args)
    {
        int[][] maps = {
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 },
                { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
                { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
                { 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
                { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }
        };
        MapInfo info=new MapInfo(maps,maps[0].length, maps.length,new Node(1, 1), new Node(4, 5));
        new AStar().start(info);
        printMap(maps);
    }

    /**
     * @Description: 打印地图
     * @Param: [maps]
     * @Return: void
     * @Author: WYF
     * @Date: 2020/4/15 11:13
    */
    public static void printMap(int[][] maps)
    {
        for (int i = 0; i < maps.length; i++)
        {
            for (int j = 0; j < maps[i].length; j++)
            {
                System.out.print(maps[i][j] + " ");
            }
            System.out.println();
        }
    }
}

//3核心代码实现:
package ai.a;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * @Author: WYF
 * @Description: 核心,基于前三个类数据结构,实现A*算法
 * @Create: 2020/4/15 11:14
 * @Version: 1.0
 */
public class AStar {
    public final static int BAR = 1; // 障碍值
    public final static int PATH = 2; // 路径
    /**
     * G值需要的横纵移动代价和斜移动代价。10就说1.0,14就说1.4
     */
    public final static int DIRECT_VALUE = 10; // 横竖移动代价
    public final static int OBLIQUE_VALUE = 14; // 斜移动代价

    Queue<Node> openList = new PriorityQueue<Node>(); // 优先队列(升序)
    List<Node> closeList = new ArrayList<Node>();

    /**
     * 判断结点是否是最终结点
     */
    private boolean isEndNode(Coord end, Coord coord) {
        return coord != null && end.equals(coord);
    }

    /**
     * 判断结点能否放入Open列表(优先队列)
     */
    private boolean canAddNodeToOpen(MapInfo mapInfo, int x, int y) {
        // 是否在地图中,超过地图大小
        if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.hight) {
            return false;
        }
        // 判断是否是不可通过的结点,该节点是障碍,不能用
        if (mapInfo.maps[y][x] == BAR) {
            return false;
        }
        // 判断结点是否存在close表,
        if (isCoordInClose(x, y)) {//如果坐标已经在close中,不能再放入open
            return false;
        }

        return true;
    }

    /**
     * 判断坐标是否在close表中,调用与自己重载的方法,多了应该非空判断
     */
    private boolean isCoordInClose(Coord coord) {
        return coord != null && isCoordInClose(coord.x, coord.y);
    }

    /**
     * 判断坐标是否在close表中,close的list,查看有没有一样的数据
     */
    private boolean isCoordInClose(int x, int y) {
        if (closeList.isEmpty()) {
            return false;
        }
        for (Node node : closeList) {
            if (node.coord.x == x && node.coord.y == y) {
                return true;
            }
        }
        return false;
    }

    /**
     * @Description: 计算H值,坐标分别取差值相加
     */
    private int calcH(Coord end, Coord coord) {
        return Math.abs(end.x - coord.x) + Math.abs(end.y - coord.y);
    }

    /**
     * @Description: 从Open列表中查找结点
     */
    private Node findNodeInOpen(Coord coord) {
        if (coord == null || openList.isEmpty()) {
            return null;
        }
        for (Node node : openList) {
            if (node.coord.equals(coord)) {
                return node;
            }
        }
        return null;
    }

    /**
     * 添加所有邻结点到open表
     */
    private void addNeighborNodeInOpen(MapInfo mapInfo, Node current) {
        int x = current.coord.x;
        int y = current.coord.y;
        // 左
        addNeighborNodeInOpen(mapInfo, current, x - 1, y, DIRECT_VALUE);
        // 上
        addNeighborNodeInOpen(mapInfo, current, x, y - 1, DIRECT_VALUE);
        // 右
        addNeighborNodeInOpen(mapInfo, current, x + 1, y, DIRECT_VALUE);
        // 下
        addNeighborNodeInOpen(mapInfo, current, x, y + 1, DIRECT_VALUE);
        // 左上
        addNeighborNodeInOpen(mapInfo, current, x - 1, y - 1, OBLIQUE_VALUE);
        // 右上
        addNeighborNodeInOpen(mapInfo, current, x + 1, y - 1, OBLIQUE_VALUE);
        // 右下
        addNeighborNodeInOpen(mapInfo, current, x + 1, y + 1, OBLIQUE_VALUE);
        // 左下
        addNeighborNodeInOpen(mapInfo, current, x - 1, y + 1, OBLIQUE_VALUE);
    }

    /**
     * 添加一个邻结点到open表
     */
    private void addNeighborNodeInOpen(MapInfo mapInfo, Node current, int x, int y, int value) {
        if (canAddNodeToOpen(mapInfo, x, y)) {
            Node end = mapInfo.end;
            Coord coord = new Coord(x, y);
            int G = current.G + value; // 计算邻结点的G值
            Node child = findNodeInOpen(coord);
            if (child == null) {
                int H = calcH(end.coord, coord); // 计算H值
                if (isEndNode(end.coord, coord)) {
                    child = end;
                    child.parent = current;
                    child.G = G;
                    child.H = H;
                } else {
                    child = new Node(coord, current, G, H);
                }
                openList.add(child);
            } else if (child.G > G) {
                child.G = G;
                child.parent = current;
                // 重新调整堆
                openList.add(child);
            }
        }
    }

    /**回溯法绘制路径*/
    private void drawPath(int[][] maps, Node end) {
        if (end == null || maps == null) {
            return;
        }
        System.out.println("总代价:" + end.G);
        /** 修改矩阵,把一路上经过的节点赋值为2,表示为路径,是从后往前推的*/
        while (end != null) {
            Coord c = end.coord;
            maps[c.y][c.x] = PATH;
            end = end.parent;
        }
    }

    /**  开始算法,循环移动结点寻找路径,设定循环结束条件,Open表为空或者最终结点在Close表*/
    public void start(MapInfo mapInfo) {
        if (mapInfo == null) {
            return;
        }
        // clean
        openList.clear();
        closeList.clear();
        // 开始搜索
        openList.add(mapInfo.start);
        moveNodes(mapInfo);
    }

    /**
     * 移动当前结点
     */
    private void moveNodes(MapInfo mapInfo) {
        while (!openList.isEmpty()) {
            Node current = openList.poll();//每次都从优先队列里面去一个最小的值,加入到open中
            closeList.add(current);
            addNeighborNodeInOpen(mapInfo, current);
            if (isCoordInClose(mapInfo.end.coord)) // 是不是终点,是就提出
            {
                drawPath(mapInfo.maps, mapInfo.end);
                break;
            }
        }
    }

}

总结
1.A算法流程图
在这里插入图片描述
2.试分析估价函数的值对搜索算法速度的影响。
个人理解,每次移动都是贪心的,这样就保证每次距离都最短;但是贪心还不够,再遇到障碍物的情况下,要进行回溯。重新去找节点。这样就实现了绕过障碍物和求得最短路径。
估价函数的形式为:f(n)=g(n)+h(n),g(n)为起点到当前位置的实际路径长度,h(n)为所在位置到终点的最佳路径的估计距离。
这个估算,保证了所找到的路径是最短路径。在我的二维平面地图中,由于我可以沿着对角线行走,所以h
(n)为所在位置到终点的直线距离。假如只能上下左右移动,那h*(n)为n到终点的水平距离与垂直距离的和。
3. 根据A*算法分析启发式搜索的特点。
启发式搜索又称为有信息搜索,它是利用问题拥有的启发信息来引导搜索,达到减少搜索范围、降低问题复杂度的目的,这种利用启发信息的搜索过程称为启发式搜索。
上面的例子中:
f(n)=g(n)+h(n)
式中:g(x)为从初始节点到节点x付出的实际代价;h(x)为从节点x到目标节点的最优路径的估计代价。启发性信息主要体现在h(x)中,其形式要根据问题的特性来确定。
虽然启发式搜索有望能够很快到达目标节点,但需要花费一些时间来对新生节点进行评价。因此,在启发式搜索中,估计函数的定义是十分重要的。如定义不当,则上述搜索算法不一定能找到问题的解,即使找到解,也不一定是最优的。

代码和图都是搜索过来,然后总结的,有侵权请联系

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A*算法是一种常用于路径搜索的启发式搜索算法,也被视为最佳优先搜索的变种。它在寻找从起点到终点的最短路径时,通常有较好的效果。 A*算法实验代码包括以下几个主要部分: 1. 创建地图:首先,需要创建一个二维表示的地图。地图可以使用数组或者邻接矩阵表示,其中不同的值代表不同的地形或者障碍物。 2. 定义节点:定义节点的结构,每个节点需要记录其位置、父节点指针、代价函数值等信息。 3. 定义启发函数:启发函数用于评估从当前节点到目标节点的估计代价,常用的启发函数有曼哈顿距离、欧几里得距离等。 4. 实现A*算法:使用优先队列等数据结构,将起点加入队列,并设置起点的代价函数值为0。然后进入循环,每次从队列中选择代价函数值最小的节点,并进行拓展。拓展的过程包括生成相邻节点、计算代价函数值、更新节点信息等。 5. 反向搜索路径:当找到目标节点后,可以沿着节点的父指针逆向搜索路径,直到起点节点,即可得到起点到终点的最短路径。 在实际运行A*算法时,需要输入起点和终点的坐标,以及地图信息。算法会根据启发函数的设置自动搜索并返回最短路径。同时,可以根据需要调整启发函数的选择,以及优化数据结构的实现,来提高算法的性能和效率。 需要注意的是,在某些特殊情况下,A*算法可能会陷入局部最优解或者产生不准确的结果,此时可以考虑使用其他路径搜索算法或者添加其他启发条件来改善算法的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值