zoukankan      html  css  js  c++  java
  • A*算法 启发式算法

    package com.ex.cy.demo4.alg.graph.ewdi;
    
    import com.ex.cy.demo4.alg.heap.IndexMinPQ;
    
    import java.util.LinkedList;
    import java.util.List;
    
    //A* 算法
    // 属于一种启发式搜索算法
    // 和Djkstra有相似之处(和BFS)
    //不同点在于,将周围顶点放入优先队列后的出队条件,不再是让g(v)(从起点到该点距离,v是被考察的顶点) 最小的先出队列
    //而是让 g(v) + h(v) 最小的先出队
    //h(v) 为 启发函数给出的值,用来估计v点到终点的估计距离,一般有1.欧几里得距离(sqrt((gx-vx)^2 + (gy-vy)^2)) 2.曼哈顿距离(abs(gx-vx)+abs(gy-vy)) (方格子,笛卡尔坐标系,只能上下左右)
    //所以每次优先出队的是最接近到达终点最短距离的点(贪心,并不一定是最优解)
    //循环退出条件是:只要遍历到终点则退出循环
    
    public class AStart {
    
        CoordinateEdgeWeightedDigraph cewd;
        IndexMinPQ<Float> impq;
        ASVertex[] vertices;
        int s;
        int g;
        Edge[] fromEdge;      //记录顶点V是从哪条边探索过来的,除了顶点,每个顶点有一条
    
        //有向无环图,起点,终点
        public AStart(CoordinateEdgeWeightedDigraph cewd, int s, int g) {
            this.cewd = cewd;
            this.s = s;
            this.g = g;
            vertices = new ASVertex[cewd.v()];
            impq = new IndexMinPQ<>(cewd.v());  //最多有全图顶点,都被放进去过
            fromEdge = new Edge[cewd.v()];
            for (int i = 0; i < cewd.v(); i++) {
                vertices[i] = new ASVertex(i, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
            }
            vertices[s].g = 0;
            vertices[s].h = 0;
    
            astartSearch();
        }
    
        //启发函数
        //估计从一个点到终点的曼哈顿距离
        public float heuristicFunc(Vertex v1, Vertex v2) {
            //欧几里得距离
    //        float dx = v2.x - v1.x;
    //        float dy = v2.y - v2.y;
    //        float dist = (float) Math.sqrt(dx * dx + dy * dy);
    //        return dist;
    
            //曼哈顿距离(省去求平方,开根号,效率会比较快)
            return Math.abs(v2.x - v1.x) + Math.abs(v2.y - v1.y);
        }
    
        public void astartSearch() {
            impq.insert(s + 1, 0f); //v顶点索引, g(v)+h(v) 起点到该点距离 + 该点到终点估计距离
    
            while (!impq.isEmpty()) {
                int v = impq.topIndex() - 1;
                float vdst = impq.delTop();
                Vertex fromV = cewd.getVertex(v);
                vertices[v].h = vdst;
    
                if (v == g)    //当前已到达终点
                    break;     //停止探索
    
                for (Edge e : cewd.adj(v)) {
                    int w = e.other(v);
                    if (vertices[w].h != Float.POSITIVE_INFINITY)    //已探索过则跳过
                        continue;
    
                    Vertex toW = cewd.getVertex(w);
                    //到下一个顶点w的实际距离
                    float gDist = vertices[v].g + e.weight;
                    float heuDist = gDist + heuristicFunc(toW, fromV);
    
                    if (impq.contain(w + 1) && impq.get(w + 1) < heuDist)
                        continue;                         //包含到w的边,但距离未缩短,则跳过当前这条边
    
                    //新增或修改
                    fromEdge[w] = e;
                    vertices[w].g = gDist;
                    if (!impq.contain(w + 1)) {               //优先队列中不存在,则直接插入新的顶点
                        impq.insert(w + 1, heuDist);
                    } else if (impq.get(w + 1) > heuDist) {   //优先队列中存在,但以当前顶点v作为相邻点距离更小
                        impq.change(w + 1, heuDist);          //更新距离
                    }
                }
            }
        }
    
        //返回从起点到终点的路径的权重总和
        public float getCost() {
            return vertices[g].g;
        }
    
        //获取从起点到终点的路径
        //并不能像djkstra找到全局最优的最短路径,因为没有遍历其他可能的路径,因为第一次探索到终点就结束循环了
        //但是优点是:找了个一个近似最优解,并用了较少的顶点探索次数,倾向性的往终点方向探索;因此可以用于很大的图中,不必遍历完所有顶点,即可找到一条近似最优解的路径,在时间效率和解的近似最优上达到了一种平衡
        public List<Edge> getEdge() {
            List<Edge> edges = new LinkedList<>();
    
            int v = g;
            Edge e = fromEdge[v];
            while (e != null) {
                edges.add(0, e);
                v = e.other(v);
                e = fromEdge[v];
            }
            return edges;
        }
    
        public static void main(String[] a) {
            CoordinateEdgeWeightedDigraph cewd = new CoordinateEdgeWeightedDigraph(6);
            //加入顶点
            //顶点索引, x y 坐标
            cewd.addVertex(0, 0, 0);
            cewd.addVertex(1, 1, 0.5f);
            cewd.addVertex(2, 0, 1);
            cewd.addVertex(3, 1, 1);
            cewd.addVertex(4, 5, 5);
            cewd.addVertex(5, 6, 6);
    
            //加入有向边,权重为两点之间的欧几里得距离
            cewd.addEdge(0, 1);
            cewd.addEdge(0, 2);
            cewd.addEdge(1, 3);
            cewd.addEdge(2, 3);
            cewd.addEdge(3, 4);
    
            System.out.println(cewd);
    
            //测试1
            AStart aStart = new AStart(cewd, 0, 4);
            System.out.println(aStart.getEdge());
            System.out.println("Cost: " + aStart.getCost());
    
            //测试2 不可达顶点
            AStart aStart2 = new AStart(cewd, 0, 5);
            System.out.println(aStart2.getEdge());
            System.out.println("Cost: " + aStart2.getCost());
        }
    }

    输出

    Ver{v=0, x=0.0, y=0.0}: 0-1 1.12, 0-2 1.00, 
    Ver{v=1, x=1.0, y=0.5}: 1-3 0.50, 
    Ver{v=2, x=0.0, y=1.0}: 2-3 1.00, 
    Ver{v=3, x=1.0, y=1.0}: 3-4 5.66, 
    Ver{v=4, x=5.0, y=5.0}: 
    Ver{v=5, x=6.0, y=6.0}: 
    
    [0-1 1.12, 1-3 0.50, 3-4 5.66]
    Cost: 7.274888
    []
    Cost: Infinity
  • 相关阅读:
    (24)码表
    (23)IO之打印流 PrintStream & Printwriter
    (22)Properties,这不会Io流中的类,但是通常和IO流中的一些流配合使用
    (21)IO流之对象的序列化和反序列化流ObjectOutputStream和ObjectInputStream
    (20)IO流之SequenceInputStream 序列流
    01.vue基础
    02.1插值操作-Mustache语法
    jQuery对节点的增删改查和一些常用的逻辑处理
    递归 阶乘
    电脑远程连接
  • 原文地址:https://www.cnblogs.com/cyy12/p/12090296.html
Copyright © 2011-2022 走看看