我正在尝试基于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值。那么通过实现这个算法我错了什么呢?
答案 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()),计算一次并在需要时重复使用该值(小时间增益但更好的代码)。