[英]Algorithm- Sum of distances between every two nodes of a Binary Search Tree in O(n)?
[英]Heuristic for A*-Algorithm with irregular distances between nodes
我目前正在研究两个节点之间的距离不规则的A *算法的实现。 包含节点的图是有向图和加权图。 每个节点都连接到至少一个其他节点,也可能存在具有不同距离的对称连接。 节点不过是标签,不包含任何特殊信息
我需要的是一种启发式方法,以尽可能准确地确定从任何节点A到另一个节点B的最短路径。 我尝试使用一种启发式方法,该方法将距离返回到节点的最近邻居,但是当然,它根本不像没有启发式方法那样有效(= Dijkstra)。
我对A *算法的实现主要包括2个类,一个是算法本身的类( AStar
),一个是节点的类( Node
)。 该代码很大程度上基于Wikipedia伪代码。
AStar.java
源代码 public class AStar {
private AStar() {}
private static Node[] reconstructPath(Map<Node, Node> paths, Node current) {
List<Node> path = new ArrayList<Node>();
path.add(0, current);
while (paths.containsKey(current)) {
current = paths.get(current);
path.add(0, current);
}
return path.toArray(new Node[0]);
}
public static Node[] calculate(Node start, Node target, IHeuristic heuristic) {
List<Node> closed = new ArrayList<Node>();
PriorityQueue<Node> open = new PriorityQueue<Node>();
Map<Node, Double> g_score = new HashMap<Node, Double>();
Map<Node, Double> f_score = new HashMap<Node, Double>();
Map<Node, Node> paths = new HashMap<Node, Node>();
g_score.put(start, 0d);
f_score.put(start, g_score.get(start) + heuristic.estimateDistance(start, target));
open.set(start, f_score.get(start));
while (!open.isEmpty()) {
Node current = null;
// find the node with lowest f_score value
double min_f_score = Double.POSITIVE_INFINITY;
for (Entry<Node, Double> entry : f_score.entrySet()) {
if (!closed.contains(entry.getKey()) && entry.getValue() < min_f_score) {
min_f_score = entry.getValue();
current = entry.getKey();
}
}
if (current.equals(target)) return reconstructPath(paths, target);
open.remove(current);
closed.add(current);
for (Node neighbor : current.getAdjacentNodes()) {
if (closed.contains(neighbor)) {
continue;
}
double tentative_g_score = g_score.get(current) + current.getDistance(neighbor);
if (!open.contains(neighbor) || tentative_g_score < g_score.get(neighbor)) {
paths.put(neighbor, current);
g_score.put(neighbor, tentative_g_score);
f_score.put(neighbor, g_score.get(neighbor) + heuristic.estimateDistance(neighbor, target));
if (!open.contains(neighbor)) {
open.set(neighbor, f_score.get(neighbor));
}
}
}
}
throw new RuntimeException("no path between " + start + " and " + target);
}
}
Node.java
源代码 public class Node {
private Map<Node, Double> distances = new HashMap<Node, Double>();
public final String name;
public Node(String name) {
this.name = name;
}
public Set<Node> getAdjacentNodes() {
return Collections.unmodifiableSet(distances.keySet());
}
public double getDistance(Node node) {
return distances.get(node);
}
public void setDistance(Node node, double distance) {
distances.put(node, distance);
}
@Override
public String toString() {
return (name == null ? "Node@" + Integer.toHexString(hashCode()) : name);
}
}
PriorityQueue.java
源代码 public class PriorityQueue<T> {
transient ArrayList<PriorityEntry<T>> elements = null;
private static final int DEFAULT_SIZE = 10;
public PriorityQueue() {
elements = new ArrayList<PriorityEntry<T>>(DEFAULT_SIZE);
}
public PriorityQueue(int initialCapacity) {
elements = new ArrayList<PriorityEntry<T>>(initialCapacity);
}
public boolean push(T element, double priority) {
PriorityEntry<T> entry = new PriorityEntry<T>(element, priority);
if (elements.contains(entry)) return false;
elements.add(entry);
elements.sort(null);
return true;
}
public void set(T element, double priority) {
PriorityEntry<T> entry = new PriorityEntry<T>(element, priority);
int index = elements.indexOf(entry);
if (index >= 0) {
elements.get(index).setPriority(priority);
} else {
elements.add(entry);
}
elements.sort(null);
}
public T peek() {
return size() <= 0 ? null : elements.get(0).getValue();
}
public T pop() {
return size() <= 0 ? null : elements.remove(0).getValue();
}
public boolean remove(T element) {
return elements.remove(new PriorityEntry<T>(element, 0));
}
public int size() {
return elements.size();
}
public boolean isEmpty() {
return elements.isEmpty();
}
public boolean contains(T element) {
return elements.contains(new PriorityEntry<T>(element, 0));
}
private class PriorityEntry<E> implements Comparable<PriorityEntry<? extends T>> {
private final E value;
private double priority = Double.MIN_VALUE;
public PriorityEntry(E value, double priority) {
this.value = value;
this.priority = priority;
}
public E getValue() {
return value;
}
public double getPriority() {
return priority;
}
public void setPriority(double priority) {
this.priority = priority;
}
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (!(o instanceof PriorityEntry)) return false;
PriorityEntry<?> entry = (PriorityEntry<?>) o;
return value.equals(entry);
}
@Override
public int compareTo(PriorityEntry<? extends T> entry) {
return (int) (getPriority() - entry.getPriority());
}
}
}
在尝试为您的问题定义启发式功能之前,请考虑在许多情况下,对目标成本的评估不正确(或不正确)与不使用启发式方法一样是自欺欺人的。
对于带有加权弧的图,您需要考虑节点中是否有一些信息可以导致获得启发式值(例如,如果您的节点是城市,则良好的估计可以是直线的长度)它们之间的直线;或者如果您的节点是数组,则它们之间的相似性度量)。 如果您的节点只是标签,并且没有可用于获取启发式值的信息,那么最好的解决方案就是根本不使用启发式。 对于大多数此类问题,这不是最坏的情况。 最好使用Dijkstra搜索(与A *相同,但使用启发式= 0),让算法从一开始就根据成本扩展节点,而不是使用不一致的错误启发式,因为在这种情况下在这种情况下,您可能正在扩展不必要的节点,这些节点似乎基于对目标成本的错误估计而很有希望。
我不知道您的图有多大,但是对于大多数问题,在使用启发式算法和完全不使用启发式算法之间,计算时间没有显着差异。 特别是在启发式错误的情况下。
我可以看到您有自己的A *实现。 我建议您看一下Hipster之类的启发式搜索库。 该库使您可以定义图形并测试不同的搜索算法,以了解适合您问题的最佳算法。 一些代码示例准确地描述了您的情况:在加权有向图中进行搜索。 这可能对您的问题很有用。
希望我的回答对您有所帮助。 问候,
在不涉及其他可能问题的情况下,我想指出一个主要问题-您缺少飞机。 如果城市之间的距离最短,您可以
您可以从飞机上推断出有意义的启发式方法。 例如,您可以从城市位置进行假设,例如应该首先查找具有最小算术距离的城市。
如果您没有飞机,那么您将没有任何手段来预测有意义的启发式方法。 A *仍然可以使用,但与穷举搜索几乎没有区别。 您可以根据重量创建平面,但是可以。
例如:
遍历权重并找到权重分位数20/60/20-现在您有了相对平面
- good weight threshold (lowest 20%)
- average weight threshold (middle 60%)
- bad weight threshold (highest 20%)
使用优先级队列,您的算法将选择好的动作,然后平均,最后选择坏的动作。 如果需要,可以有3个以上细分。
提醒一下:A *快速返回足够好的结果。 使用详尽搜索可以找到最佳解决方案,但是如果问题规模增大,它将以指数级的速度变慢。
要添加到上面的@kiheru评论。 您的解决方案将仅与提供的启发式方法一样好。
如果以下行和heuristic.estimate,则范围太窄。 该算法将迅速达到局部最小值。 或者,如果不允许使用启发式算法,则该算法将导致无解或不正确的随机解。
f_score.put(start, g_score.get(start) + heuristic.estimateDistance(start, target));
请仔细查看您的启发式方法,并确认它是可以接受的。 如果可以接受,则可能需要对其进行改进以提供更准确的估算。
在您的节点类的情况下,如果它们代表2D空间中节点的位置,则似乎具有X和Y,也许您可以根据从X和Y值计算得出的节点之间的直线距离使用启发式方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.