A-Star算法像Dijkstra一样运行并且传递错误的结果

时间:2013-09-03 12:21:46

标签: java algorithm openstreetmap a-star

我正在尝试基于OSM数据在Java中实现A-Star。我的问题是我的实现无法正常工作。首先,路径不是最短的。其次,封闭式列表最后包含了多达1/3的节点,如Dijkstra。多数民众赞成不是我所期望的。

这是我的A-Star代码,它基于Wikipedia Pseudocode

public Object[] executeAstar(ArrayList<Arclistentry> data, NodeD start, NodeD dest,long[] nodenur)
{
    openlist = new PriorityQueue<NodeD>(1,comp);
    closedlist.clear();
    openlist.offer(start);
    start.setg(0);
    start.seth(calccost(start, dest));
    start.setf(start.getg()+start.geth());
    while(!openlist.isEmpty())
    {
        NodeD currentnode = openlist.poll();
        if(currentnode.getnodenumber() == dest.getpredessor())
        {
            closedlist.add(currentnode);
            return drawway(closedlist, start, dest);
        }
        closedlist.add(currentnode);
        ArrayList<Arclistentry> entries = neighbors.get((int)currentnode.getnodenumber()-1);
        for(Arclistentry aentry:entries)
        {
            NodeD successor = new NodeD(aentry.getnode(),aentry.getstart(), aentry.getcoorddest());
                float tentative_g = currentnode.getg()+calccost(currentnode,successor);//+aentry.getcost();
                if(contains(successor, closedlist))
                {
                    continue;
                }
                if((contains(successor,openlist))&& tentative_g >= aentry.getcost())
                {
                    continue;
                }

                        if(!contains(successor, openlist))
                        {
                            successor.setpredessor(currentnode.getnodenumber());
                            successor.setg(tentative_g);
                            successor.seth(calccost(successor, dest));
                            successor.setf(successor.getg()+successor.geth());
                            openlist.offer(successor);
                        }
                        else
                        {
                            openlist.remove(successor);
                            successor.setpredessor(currentnode.getnodenumber());
                            successor.setg(tentative_g);
                            successor.seth(calccost(successor, dest));
                            successor.setf(successor.getg()+successor.geth());
                            openlist.offer(successor);
                        }
        }
    }
    return drawway(closedlist,start, dest);
}

我的启发式算法将使用欧几里德距离计算。但是,为了考虑节点的成本,成本乘以启发式结果。我的数据结构包含以下内容:

private long nodenumber;
private long predessor;
private float label;
private float f;
private float g;
private float h;
private double[] coord = new double[2];

public NodeD(long nodenr, long predessor, double[] coor)
{
    this.nodenumber = nodenr;
    this.predessor = predessor;
    this.coord = coor;
}
public NodeD(long nodenr, long predessor, float label)
{
    this.nodenumber = nodenr;
    this.predessor = predessor;
    this.label = label;
}

对于arclist我使用以下内容:

private long start;
private long dest_node;
private float cost_;
private double[]coordstart = new double[2];
private double[]coorddest = new double[2];

包含优先级队列的功能:

public boolean contains(NodeD o, PriorityQueue<NodeD> al)
 {
     Iterator<NodeD> e = al.iterator();
     if (o==null)
     {
          while (e.hasNext())
          {
             if (e.next()==null)
             {
                 return true;
             }
          }
     }
     else
     {
         while (e.hasNext())
         {
             NodeD t = e.next();
             if(t.equals(null))
             {
                 return false;
             }
             if (((o.getnodenumber()==t.getnodenumber()) & (o.getpredessor()==t.getpredessor()))||(o.getnodenumber()==t.getpredessor() & o.getpredessor()==t.getnodenumber()))
             {
                 return true;
             }
         }
             return false;
     }
     return false;
 }

并包含ArrayList(因为它没有使用ArrayList.contains函数检测到

public boolean contains(NodeD o, ArrayList<NodeD> al) {
           return indexOf(o,al) >= 0;
       }

public int indexOf(NodeD o, ArrayList<NodeD> al) {
        if (o == null) {
            for (int i = 0; i < al.size(); i++)
                if (al.get(i)==null)
                    return i;
        } else {
                   for (int i = 0; i < al.size(); i++)
                   {
                      if ((o.getpredessor()==al.get(i).getpredessor())) //(o.getnodenumber()==al.get(i).getnodenumber()) &&
                      {
                           return i;
                      }
                       else if((o.getpredessor()==al.get(i).getnodenumber())&&(o.getnodenumber()==al.get(i).getpredessor()))
                      {
                          return i;
                      }
                   }
               }
        return -1;
}

问题是该算法正在访问所有节点。另一个问题是排序的开放列表,它推动当前节点的邻居,因为它们具有较低的f值。那么通过实现这个算法我错了什么呢?

1 个答案:

答案 0 :(得分:1)

回顾我们之前的所有答案:

  • 确保A *估算值较低,否则会错误地跳过部分

  • 不要遍历所有节点以确定数组中当前节点边集的边缘索引

  • 创建要放入队列/集的新对象时,应对节点的属性进行检查
  • 如果您的重点是速度,请尽快通过中止非感兴趣的搜索来避免尽可能多的工作

我仍然不确定这一行:

if((contains(successor,openlist))&& tentative_g >= aentry.getcost())

我认为您要做的是避免在队列中有更好的值时向队列添加新节点。但是,tentative_g是从起始节点到当前节点的路径长度,而aentry.getcost似乎是您正在放松的边缘的长度。这对我来说似乎不对...尝试检索正确的(旧)值以与新的暂定标签进行比较。

最后,对于您当前的代码,我还会进行以下更改:

  • 将HashSet用于您的封闭列表。每次检查一个节点是否在那里时,你必须全部检查它们,这不是那么有效...尝试通过覆盖NodeD对象的哈希函数来使用HashSet。内置的contains-function比你当前的方法快得多。您的公开名单也可以提出类似的论点。您不能将PQ更改为集合,但可以省略包含检查。如果添加具有错误优先级的节点,您将始终首先轮询正确的优先级(因为它是PQ),然后在轮询错误的优先级时,可以跳过它。这是一个小的优化,它将PQ的大小换算为PQ查找操作

  • 避免重新计算东西(主要是calccost()),计算一次并在需要时重复使用该值(小时间增益但更好的代码)。

  • 尝试通过将多个行放在正确的行上来避免使用相同代码的多行(例如,如果你有类似if(..)的话,可以将2个closedlist.add函数合并到if条件上方的1个add-call中{doA(); doB()} else {doA(); doC();}尝试将doA()放在if之前为了易读性