繁体   English   中英

节点间距离不规则的A *算法的启发式

[英]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城市b的成本的数值
  • 平面 -描述环境,例如:城市位置(在您的正方形网格中)

您可以从飞机上推断出有意义的启发式方法。 例如,您可以从城市位置进行假设,例如应该首先查找具有最小算术距离的城市。

如果您没有飞机,那么您将没有任何手段来预测有意义的启发式方法。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM