• 数据结构与算法简记--A*算法


    A*算法


     问题

    实现游戏地图中自动寻路功能

    分析

    可使用Dijkstra算法计算最短路径,让游戏角色按路径行走;

    但Dijkstra算法是BFS的思想,会有盲目计算,效率低;

    寻路不用非得走最短路径,可走次短即可

    鉴于平衡效率与效果,使用优化版Dijkstra算法:A*算法

    A*算法

    优化版Dijkstra算法

    启发式搜索

    引入启发函数(heuristic function)来优化出队顺序

    根据启发函数和Dijkstra算法顶点距离g(i),得到估价函数(evaluation function):f(i)=g(i)+h(i)

    此例中的启发函数选择

      • 欧几里得距离:直线距离,但会涉及比较耗时的开根号计算
      • 曼哈顿距离(Manhattan distance):两点之间横纵坐标的距离之和。计算的过程只涉及加减法、符号位反转,所以比欧几里得距离更加高效
      • int hManhattan(Vertex v1, Vertex v2) { // Vertex表示顶点,后面有定义
          return Math.abs(v1.x - v2.x) + Math.abs(v1.y - v2.y);
        }

    实现

    顶点 Vertex 类的定义,相比Dijkstra 算法中的定义,多了 x,y 坐标,以及估价值 f(i) 。

    • private class Vertex {
        public int id; // 顶点编号ID
        public int dist; // 从起始顶点,到这个顶点的距离,也就是g(i)
        public int f; // 新增:f(i)=g(i)+h(i)
        public int x, y; // 新增:顶点在地图中的坐标(x, y)
        public Vertex(int id, int x, int y) {
          this.id = id;
          this.x = x;
          this.y = y;
          this.f = Integer.MAX_VALUE;
          this.dist = Integer.MAX_VALUE;
        }
      }
      // Graph类的成员变量,在构造函数中初始化
      Vertex[] vertexes = new Vertex[this.v];
      // 新增一个方法,添加顶点的坐标
      public void addVetex(int id, int x, int y) {
        vertexes[id] = new Vertex(id, x, y)
      }

    A* 算法是对 Dijkstra 算法的简单改造,实际上稍微改动几行代码,就能把 Dijkstra 算法的代码实现,改成 A* 算法的代码实现,

    相比 Dijkstra 算法的代码实现,主要有 3 点区别:

      • 优先级队列构建的方式不同。A* 算法是根据 f 值来构建优先级队列,而 Dijkstra 算法是根据 g 值;
      • A* 算法在更新顶点 dist 值的时候,会同步更新 f 值;
      • 循环结束的条件也不一样。Dijkstra 算法是在终点出队列的时候才结束,A* 算法是一旦遍历到终点就结束。
    • public void astar(int s, int t) { // 从顶点s到顶点t的路径
        int[] predecessor = new int[this.v]; // 用来还原路径
        // 按照vertex的f值构建的小顶堆,而不是按照dist
        PriorityQueue queue = new PriorityQueue(this.v);
        boolean[] inqueue = new boolean[this.v]; // 标记是否进入过队列
        vertexes[s].dist = 0;
        vertexes[s].f = 0;
        queue.add(vertexes[s]);
        inqueue[s] = true;
        while (!queue.isEmpty()) {
          Vertex minVertex = queue.poll(); // 取堆顶元素并删除
          for (int i = 0; i < adj[minVertex.id].size(); ++i) {
            Edge e = adj[minVertex.id].get(i); // 取出一条minVetex相连的边
            Vertex nextVertex = vertexes[e.tid]; // minVertex-->nextVertex
            if (minVertex.dist + e.w < nextVertex.dist) { // 更新next的dist,f
              nextVertex.dist = minVertex.dist + e.w;
              nextVertex.f 
                 = nextVertex.dist+hManhattan(nextVertex, vertexes[t]);
              predecessor[nextVertex.id] = minVertex.id;
              if (inqueue[nextVertex.id] == true) {
                queue.update(nextVertex);
              } else {
                queue.add(nextVertex);
                inqueue[nextVertex.id] = true;
              }
            }
            if (nextVertex.id == t) { // 只要到达t就可以结束while了
              queue.clear(); // 清空queue,才能推出while循环
              break; 
            }
          }
        }
        // 输出路径
        System.out.print(s);
        print(s, t, predecessor); // print函数请参看Dijkstra算法的实现
      }

     

     

  • 相关阅读:
    QuickFlash
    第五课:类的封装性 M
    第六课:构造方法、匿名对象 M
    第四课:面向对象(基础) M
    第八课:引用传递 M
    第七课:String类 M
    第二课:数组 M
    第三课:方法 M
    ASP .Net的应用程序域(The Application Domain)
    js 解数独程序
  • 原文地址:https://www.cnblogs.com/wod-Y/p/12157340.html
Copyright © 2020-2023  润新知