【问题标题】:Heuristic for A*-Algorithm with irregular distances between nodes节点间距离不规则的 A* 算法的启发式
【发布时间】:2015-01-30 16:13:45
【问题描述】:

我目前正在实现A* Algorithm,两个节点之间的距离不规则。包含节点的图是有向加权图。每个节点至少连接到一个其他节点,也可能存在不同距离的对称连接。节点只不过是一个标签,不包含任何特殊信息

我需要的是一种启发式方法,以尽可能准确地确定从任何节点 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());
        }
    }
}

【问题讨论】:

标签: java algorithm path-finding a-star heuristics


【解决方案1】:

在尝试为您的问题定义启发式函数之前,请考虑到在许多情况下,对目标成本的错误(或不正确)估计与根本不使用启发式一样会弄巧成拙。

对于带有加权弧的图,您需要考虑节点中是否有一些信息可以导致获得启发式值(例如,如果您的节点是城市,那么好的估计可以是长度它们之间的直线;或者如果您的节点是数组,则它们之间的相似性度量)。如果您的节点只是标签并且没有可用于获取启发式值的信息,那么最好的解决方案可能根本不使用启发式。对于大多数此类问题,这并不是最糟糕的情况。最好使用 Dijkstra 搜索(与 A* 相同,但使用启发式 = 0),让算法从一开始就根据成本扩展节点,而不是使用不一致的坏启发式搜索,因为在这个在这种情况下,您可能会扩展不必要的节点,这些节点基于对目标成本的错误估计而看起来很有希望。

我不知道您的图表有多大,但对于大多数问题,使用启发式算法和完全不使用它之间的计算时间没有显着差异。特别是在启发式错误的情况下。

我可以看到您有自己的 A* 实现。我建议你看看像Hipster 这样的启发式搜索库。该库允许您定义图表并测试不同的搜索算法,以了解最适合您问题的算法。一些代码示例准确地描述了您的情况:在加权有向图中搜索。它可能对您的问题有用。

希望我的回答有帮助。问候,

【讨论】:

    【解决方案2】:

    在不涉及其他可能的问题的情况下,我想指出主要问题 - 你缺少飞机。在城市之间的最短距离问题的情况下,你有

    • 节点 - 城市
    • weight - 描述从 city acity b 的成本的数值
    • 平面 - 描述环境,例如:城市位置(在您的方格中)

    从平面你可以推断出有意义的启发式。例如,您可以从城市位置进行假设,例如应该首先查看算术距离最低的城市。

    如果您没有平面,则无法预测有意义的启发式。 A* 仍然有效,但它与穷举搜索几乎没有区别。您可以根据重量创建平面,但是它。

    例如:

    遍历权重并找到权重分位数 20/60/20 - 现在您有了一个相对平面

    - good weight threshold (lowest 20%) 
    - average weight threshold (middle 60%)
    - bad weight threshold (highest 20%)
    

    使用优先队列,您的算法会选择好的动作,然后是平均的,最后是坏的。 如果您愿意,您可以拥有 3 个以上的段。


    提醒一下:A* 可以快速返回足够好的结果。使用穷举搜索,您可以找到最佳解决方案,但如果问题规模增长,它会以指数方式变慢。

    【讨论】:

      【解决方案3】:

      添加到上面的@kiheru 评论。您的解决方案只会与所提供的启发式一样好。

      如果以下行和 heuristic.estimate,范围太窄。该算法将很快达到局部最小值。或者,如果启发式不可接受,则算法将导致无解或不正确的随机解。

          f_score.put(start, g_score.get(start) + heuristic.estimateDistance(start, target));
      

      仔细查看您的启发式并确认它是可接受的。如果可以接受,则可能需要对其进行改进以提供更准确的估计。

      【讨论】:

      • 我知道搜索算法与提供的启发式算法一样好。我不明白为什么你引用的那行范围太窄了。
      • 很抱歉给您带来了困惑。我不知道这条线的范围太窄了。我的意图是指出以下几点。据我了解这个问题,您从算法中得到的结果很差。由于您的大部分代码都遵循 Wikipedia 上的伪代码,因此问题可能出在启发式函数中。如果启发式给出坏/差的结果,A* 将不会提供最佳解决方案。
      • 我知道问题在于启发式。我唯一需要的实际上是启发式。最近邻启发式(参见问题上的 cmets)效果不佳(不足为奇),曼哈顿启发式根本不起作用,因为(参见 BrendanM 的回答中的 cmets)我节点的坐标(现在已删除) ) 与两个节点之间的实际距离无关。
      【解决方案4】:

      在您的节点类的情况下,如果它们代表节点在 2D 空间中的位置,它似乎有一个 X 和 Y,也许您可​​以使用基于从 X 和 Y 值计算的节点之间的直线距离的启发式算法.

      【讨论】:

      • 你是对的,节点包含二维坐标。这仍然来自我使用节点表示正方形网格中的正方形的版本,我可能应该删除它以使其更清晰。通过坐标计算距离的问题是两个节点之间的距离与它们的相对坐标无关
      • 啊好吧可能不应该假设它们与节点位置有关
      猜你喜欢
      • 2014-06-05
      • 1970-01-01
      • 2016-12-14
      • 1970-01-01
      • 1970-01-01
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      相关资源
      最近更新 更多