![](/img/trans.png)
[英]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.