A *(A星)算法优化

时间:2014-01-16 12:10:21

标签: java algorithm path-finding a-star

我是学生,我和我的团队必须模拟学生在校园里的行为(比如制作“朋友小组”)走路等。为了找到学生必须去的路径,我使用了A *算法(因为我发现它是最快的寻路算法之一)。不幸的是,我们的模拟不能流畅地运行(在连续迭代之间需要1-2秒)。我想优化算法,但我不知道我能做些什么。如果有可能优化我的A *算法,你们可以帮助我并与我分享信息吗?这里是代码:

  public LinkedList<Field> getPath(Field start, Field exit) {


    LinkedList<Field> foundPath = new LinkedList<Field>();
    LinkedList<Field> opensList= new LinkedList<Field>();
    LinkedList<Field> closedList= new LinkedList<Field>();
    Hashtable<Field, Integer> gscore = new Hashtable<Field, Integer>();
    Hashtable<Field, Field> cameFrom = new Hashtable<Field, Field>();
    Field x = new Field();
    gscore.put(start, 0);
    opensList.add(start);
    while(!opensList.isEmpty()){

        int min = -1;
        //searching for minimal F score
        for(Field f : opensList){
            if(min==-1){
                min = gscore.get(f)+getH(f,exit);
                x = f;
            }else{
                int currf = gscore.get(f)+getH(f,exit);
                if(min > currf){
                    min = currf;
                    x = f;
                }
            }
        }
        if(x == exit){
            //path reconstruction
            Field curr = exit;
            while(curr != start){
                foundPath.addFirst(curr);
                curr = cameFrom.get(curr);
            }
            return foundPath;
        }
        opensList.remove(x);
        closedList.add(x);
        for(Field y : x.getNeighbourhood()){
            if(!(y.getType()==FieldTypes.PAVEMENT ||y.getType() == FieldTypes.GRASS) || closedList.contains(y) || !(y.getStudent()==null))
                            {
                continue;
            }
            int tentGScore = gscore.get(x) + getDist(x,y);
            boolean distIsBetter = false;
            if(!opensList.contains(y)){
                opensList.add(y);
                distIsBetter = true;
            }else if(tentGScore < gscore.get(y)){
                distIsBetter = true;
            }
            if(distIsBetter){
                cameFrom.put(y, x);
                gscore.put(y, tentGScore);
            }               
        }
    }

    return foundPath;
}

 private int getH(Field start, Field end){
    int x;
    int y;
    x = start.getX()-end.getX();
    y = start.getY() - end.getY();
    if(x<0){
        x = x* (-1);
    }
    if(y<0){
        y = y * (-1);
    }
    return x+y;
}
private int getDist(Field start, Field end){
    int ret = 0;
    if(end.getType() == FieldTypes.PAVEMENT){
        ret = 8;
    }else if(start.getX() == end.getX() || start.getY() == end.getY()){
        ret = 10;
    }else{
        ret = 14;
    }

    return ret;
}

// EDIT

这是我从jProfiler获得的:

jProfiler

所以getH是瓶颈吗?也许记住场上的H得分会是一个好主意吗?

6 个答案:

答案 0 :(得分:6)

您可以使用不同的算法优化问题,以下页面说明并比较了许多不同的算法和启发式方法:

  • A *
  • IDA *
  • Djikstra
  • JumpPoint
  • ...

http://qiao.github.io/PathFinding.js/visual/ enter image description here

答案 1 :(得分:6)

链接列表不是开放集的良好数据结构。您必须从中找到具有最小F的节点,您可以在O(n)中搜索列表,也可以在O(n)中的有序位置插入,无论哪种方式都是O(n)。使用堆只有O(log n)。更新G分数将保持为O(n)(因为您必须先找到节点),除非您还从节点向堆中的索引添加了HashTable。

链接列表也不是封闭集的良好数据结构,您需要快速“包含”,即链接列表中的O(n)。你应该使用HashSet。

答案 2 :(得分:2)

从您的实现看来,您似乎正在使用天真的A *算法。使用以下方式: -

  
      
  1. A *是使用类似于BFS的优先级队列实现的算法。

  2.   
  3. 在每个节点评估启发式函数,以定义其适合作为下一个要访问的节点的适合度。

  4.   
  5. 当访问新节点时,其相邻的未访问节点将以其启发式值作为键添加到队列中。

  6.   
  7. 这样做直到队列中的每个启发式值小于(或大于)目标状态的计算值。

  8.   

答案 3 :(得分:1)

  1. 使用探查器查找实施的瓶颈。 ex. jprofiler is easy to use
  2. 在算法可以同时运行的区域中使用线程。
  3. 将您的JavaVM配置为运行得更快。 Allocate more RAM

答案 4 :(得分:1)

a)如上所述,您应该在A *中使用堆 - 基本二进制堆或配对堆,理论上应该更快。

b)在较大的地图中,总是需要一些时间让算法运行(即,当您请求路径时,它只需要花费一些时间)。可以做的是在路径计算时使用一些本地导航算法(例如,“直接运行到目标”)。

c)如果您有合理数量的位置(例如,在导航网格中)和代码开头的某个时间,为什么不使用Floyd-Warshall的算法?使用它,您可以在O(1)中找到下一步的信息。

答案 5 :(得分:0)

我构建了一个新的寻路算法。称为Fast *或Fastaer,它是像A *一样的BFS,但比A *更快更有效,准确度为90%A *。请参阅此链接以获取信息和演示。

https://drbendanilloportfolio.wordpress.com/2015/08/14/fastaer-pathfinder/

它有一个快速贪婪的线描示器,使路径更直。 演示文件拥有一切。使用演示进行性能指标时检查任务管理器。到目前为止,在构建此结果时,其分析结果具有最大存活生成4和低至零GC时间。