A *算法完成但返回次优路径

时间:2019-05-07 05:54:34

标签: java algorithm a-star

算法:

使用支持更改优先级操作的PQ。

假定所有ADT正常工作。

问题示例:使用以下运算和权重找到从整数x到整数y的最短运算路径;加/减1:1,乘以/除以2:5,平方:10。

经过其他类型的图和输入的测试,有时它获得最短路径,有时它获得次优,有时超时。

import java.util.*;



/**
 * Represents a graph of vertices.
 */
public interface AStarGraph<Vertex> {

    List<WeightedEdge<Vertex>> neighbors(Vertex v);

    double estimatedDistanceToGoal(Vertex s, Vertex goal);

}

public interface ShortestPathsSolver<Vertex> {
    SolverOutcome outcome();
    List<Vertex> solution();
    double solutionWeight();
    int numStatesExplored();
    double explorationTime();
}

public interface ExtrinsicMinPQ<T> {
    /* Inserts an item with the given priority value. */
    void add(T item, double priority);
    /* Returns true if the PQ contains the given item. */
    boolean contains(T item);
    /* Returns the minimum item. */
    T getSmallest();
    /* Removes and returns the minimum item. */
    T removeSmallest();
    /* Changes the priority of the given item. Behavior undefined if the item doesn't exist. */
    void changePriority(T item, double priority);
    /* Returns the number of itemToPriority in the PQ. */
    int size();
}

public enum SolverOutcome {
    SOLVED, TIMEOUT, UNSOLVABLE
}


public class ArrayHeapMinPQ<T> implements ExtrinsicMinPQ<T> {
    private ArrayList<PriorityNode> heap;
    int count;
    private HashMap<T, PriorityNode> items;

    public ArrayHeapMinPQ() {
        heap = new ArrayList<>();
        heap.add(0, new PriorityNode(null, Double.NEGATIVE_INFINITY));
        count = 0;  // For convenient math
        items = new HashMap<>();
    }

    @Override
    public void add(T item, double priority) {
        if(contains(item)){
            throw new IllegalArgumentException();
        }
        PriorityNode pn = new PriorityNode(item, priority);
        if(count == 0){
            heap.add(1, pn);
            count++;
        }else{
            heap.add(count+1, pn);
            swim(count+1);
            count++;

        }
        items.put(item, pn);

    }

    private void swap(int i, int j){
        Collections.swap(heap, i, j);
    }
    private void swim(int i){
        while(i > 1){
            PriorityNode cur = heap.get(i);
            if((cur.compareTo(heap.get(i/2)) >= 0)){
                break;
            }
            swap(i, i/2);
            i = i/2;
        }

    }

    private void sink(int k){
        while (2*k <= size()-1) {
            if(2*k+1 <= size()-1) {
                if (heap.get(2 * k).compareTo(heap.get(2 * k + 1)) < 0) {
                    if (heap.get(k).compareTo(heap.get(2 * k)) > 0) {
                        swap(k, 2 * k);
                        k = 2 * k;
                        continue;
                    }
                } else if(heap.get(k).compareTo(heap.get(2*k+1)) > 0){
                    swap(2*k+1, k);
                    k = 2*k +1;
                    continue;}
            }
            else if (heap.get(k).compareTo(heap.get(2 * k)) > 0) {
                swap(k, 2 * k);
                k = 2 * k;
                continue;}
            break;
        }
    }

    @Override
    public int size(){
        return heap.size();
    }

    public PriorityNode getRoot(){
        return heap.get(1);
    }

    @Override
    public boolean contains(T item) {
        return items.containsKey(item);}

    @Override
    public T getSmallest() {
        if(heap.size() == 0){
            throw new NoSuchElementException();
        }
        return getRoot().getItem();
    }

    @Override
    public T removeSmallest() {
        if(heap.size() == 1){
            throw new NoSuchElementException();
        }
        T item = heap.get(1).item;
        swap(1, size()-1);
        heap.remove(size()-1);
        if(size() > 1){
            sink(1);
        }
        items.remove(item);
        count--;
        return item;
    }

    public boolean isEmpty(){
        return heap.size() == 1;
    }


    public int getCount() {
        return count;
    }

    @Override
    public void changePriority(T T, double priority) {
        if(heap.size() == 0){
            throw new NoSuchElementException();
        }
        PriorityNode tochange = items.get(T);
        double prioritysearch = tochange.getPriority();
        double currprior = getRoot().getPriority();
        int left = 0;
        int right = 0;
        if((prioritysearch != currprior)){
            if (currprior > prioritysearch){
                add(T, priority);
                return;
            }
            left = heapTraverse(prioritysearch, tochange, 2);
            right = heapTraverse(prioritysearch, tochange, 3);

        }
        if(left == -1 && right == -1){
            throw new NoSuchElementException();
        }
        else if(left > 0){
            PriorityNode toChange = heap.get(left);
            toChange.setPriority(priority);
            if(priority < heap.get(left/2).getPriority()){
                swim(left);
            }else
                sink(left);
        }

        else {
            PriorityNode toChange = heap.get(right);
            toChange.setPriority(priority);
            if (priority < heap.get(right / 2).getPriority()) {
                swim(right);
            } else
                sink(right);
        }
    }

    private int heapTraverse(double priority, PriorityNode node, int index){
        if(index > heap.size()-1){
            return -1;
        }
        PriorityNode curr = heap.get(index);
        double currprior = curr.getPriority();
        if(currprior == priority && node.equals(curr)){
            return index;
        } else if(currprior > priority){
            return -1;
        }else{
            if(heapTraverse(priority, node, index*2) == -1){

                return heapTraverse(priority, node, index*2 +1);}

            else {return heapTraverse(priority, node, index*2);}

        }
    }

    private class PriorityNode implements Comparable<PriorityNode> {
        private T item;
        private double priority;

        PriorityNode(T e, double p) {
            this.item = e;
            this.priority = p;
        }

        T getItem() {
            return item;
        }

        double getPriority() {
            return priority;
        }

        void setPriority(double priority) {
            this.priority = priority;
        }

        @Override
        public int compareTo(PriorityNode other) {
            if (other == null) {
                return -1;
            }
            return Double.compare(this.getPriority(), other.getPriority());
        }

        @Override
        public boolean equals(Object o) {
            if( o == null) {
                throw new NullPointerException();
            }
            if (o.getClass() != this.getClass()) {
                return false;
            } else {
                return ((PriorityNode) o).getItem().equals(getItem());
            }
        }

        @Override
        public int hashCode() {
            return item.hashCode();
        }
    }

public class WeightedEdge<Vertex> {
    private Vertex v;
    private Vertex w;
    private double weight;

    public WeightedEdge(Vertex v, Vertex w, double weight) {
        this.v = v;
        this.w = w;
        this.weight = weight;
    }
    public Vertex from() {
        return v;
    }
    public Vertex to() {
        return w;
    }
    public double weight() {
        return weight;
    }
}

public class SolutionPrinter {
    /** Summarizes the result of the search made by this solver without actually
     *  printing the solution itself (if any).
     */
    public static <Vertex> void summarizeOutcome(ShortestPathsSolver<Vertex> solver) {
        summarizeSolution(solver, "", false);
    }

/** Summarizes the result of the search made by this solver and also
 *  prints each vertex of the solution, connected by the given delimiter,
 *  e.g. delimiter = "," would return all states separated by commas.
 */
public static <Vertex> void summarizeSolution(ShortestPathsSolver<Vertex> solver,
                                              String delimiter) {
    summarizeSolution(solver, delimiter, true);
}

private static <Vertex> String solutionString(ShortestPathsSolver<Vertex> solver,
                                              String delimiter) {
    List<String> solutionVertices = new ArrayList<>();
    for (Vertex v : solver.solution()) {
        solutionVertices.add(v.toString());
    }
    return String.join(delimiter, solutionVertices);
}

private static <Vertex> void summarizeSolution(ShortestPathsSolver<Vertex> solver,
                                               String delimiter, boolean printSolution) {

    System.out.println("Total states explored in " + solver.explorationTime()
            + "s: " + solver.numStatesExplored());

    if (solver.outcome() == SolverOutcome.SOLVED) {
        List<Vertex> solution = solver.solution();
        System.out.println("Search was successful.");
        System.out.println("Solution was of length " + solution.size()
                + ", and had total weight " + solver.solutionWeight() + ":");
        if (printSolution) {
            System.out.println(solutionString(solver, delimiter));
        }
    } else if (solver.outcome() == SolverOutcome.TIMEOUT) {
        System.out.println("Search timed out, considered " + solver.numStatesExplored()
                + " vertices before timing out.");
    } else { // (solver.outcome() == SolverOutcome.UNSOLVABLE)
        System.out.println("Search determined that the goal is unreachable from source.");
    }
}

}

公共类AStarSolver实现ShortestPathsSolver {

private final AStarGraph<Vertex> graph;
private Vertex source;
private Vertex dest;
private SolverOutcome result;
private HashMap<Vertex, Double> distTo = new HashMap<>();
private ArrayHeapMinPQ<Vertex> fringe = new ArrayHeapMinPQ<>();
private HashMap<Vertex, WeightedEdge<Vertex>> edgeTo = new HashMap<>(); // answers the question which vertex to ge to this vertex
private double solutionweight;
private List<Vertex> solution;
private ArrayList<Vertex> marked = new ArrayList<>();
private double timetosolve;
private int numofstates = 0;


public AStarSolver(AStarGraph<Vertex> input, Vertex start, Vertex end, double timeout ){

    graph = input;
    source = start;
    dest = end;
    if(start.equals(end)){
        solutionweight = 0;
        solution = List.of(start);
        result = SolverOutcome.SOLVED;
        numofstates = 0;
        timetosolve = 0;
        return;
    }
    fringe.add(start, 0.0);
    distTo.put(start, 0.0);
    while (!fringe.isEmpty()) {
        Vertex src = fringe.removeSmallest();
        numofstates++;
        marked.add(src);

        List<WeightedEdge<Vertex>> neighbors = graph.neighbors(src);
        for(WeightedEdge<Vertex> e: neighbors){

            double heur = graph.estimatedDistanceToGoal(e.to(), dest);
            if ((heur == Double.POSITIVE_INFINITY || marked.contains(e.to())) && !e.to().equals(dest)) {
                continue;
            }
            double distFr = distTo.get(e.from()) + e.weight();

            if(!distTo.containsKey(e.to())){
                distTo.put(e.to(), distFr);
            }
            if (!fringe.contains(e.to())) {
                fringe.add(e.to(),  distFr + heur);
                edgeTo.put(e.to(), e);
            }

            if (distTo.get(e.to()) > distFr) {
                fringe.changePriority(e.to(), heur + distFr);
                edgeTo.put(e.to(), e);
                distTo.put(e.to(), distFr);
            }
            if (e.to().equals(dest)) {
                solutionweight = distTo.get(e.to());
                solution = pathTracer(e);
                timetosolve = sw.elapsedTime();
                result = SolverOutcome.SOLVED;
                return;
            }

            if (e.to().equals(dest)) {
                solutionweight = distTo.get(e.to());
                solution = pathTracer(e);
                timetosolve = sw.elapsedTime();
                result = SolverOutcome.SOLVED;
                return;
            }


        }
        if (timeout < sw.elapsedTime()){
            result = SolverOutcome.TIMEOUT;
            return;
        }
    }
    result = SolverOutcome.UNSOLVABLE;
    solution = List.of();

}

private List<Vertex> pathTracer(WeightedEdge<Vertex> e) {
    ArrayList<Vertex> path = new ArrayList<>();
    path.add(e.to());
    path.add(e.from());
    while (!e.from().equals(source)) {
        e = edgeTo.get(e.from());
        path.add(e.from());
    }
    Collections.reverse(path);
    return path;
}

@Override
public SolverOutcome outcome() {
    return result;
}

@Override
public List solution() {
    return solution;
}

@Override
public double solutionWeight() {
    return solutionweight;
}

@Override
public int numStatesExplored() {
    return numofstates;
}

@Override
public double explorationTime() {
    return timetosolve;
}

public class IntegerHopGraph implements AStarGraph<Integer> {

    @Override
    public List<WeightedEdge<Integer>> neighbors(Integer v) {
        ArrayList<WeightedEdge<Integer>> neighbors = new ArrayList<>();
        neighbors.add(new WeightedEdge<>(v, v * v, 10));
        neighbors.add(new WeightedEdge<>(v, v * 2, 5));
        neighbors.add(new WeightedEdge<>(v, v / 2, 5));
        neighbors.add(new WeightedEdge<>(v, v - 1, 1));
        neighbors.add(new WeightedEdge<>(v, v + 1, 1));
        return neighbors;
    }

    @Override
    public double estimatedDistanceToGoal(Integer s, Integer goal) {
        // possibly fun challenge: Try to find an admissible heuristic that
        // speeds up your search. This is tough!
        return 0;
    }
}



public class DemoIntegerHopPuzzleSolution {
    public static void main(String[] args) {
        int start = 17;
        int goal = 111;

        IntegerHopGraph ahg = new IntegerHopGraph();

        ShortestPathsSolver<Integer> solver = new AStarSolver<>(ahg, start, goal, 10);
        SolutionPrinter.summarizeSolution(solver, " => ");

    }
}

}

要从x = 11到y = 117,算法给出以下结果:

在0.019秒内探索的全部状态:110

搜索成功。

解决方案的长度为7,总重量为19.0:

17 => 16 => 15 => 225 => 224 => 223 => 111

正确的结果应该是:

在0.018秒内探索的全部状态:338 <---可能会有所不同。

搜索成功。

解决方案的长度为6,总重量为18.0:

17 => 16 => 15 => 225 => 112 => 111

1 个答案:

答案 0 :(得分:0)

感谢所有帮助人员,但我知道了。我的算法过早终止。它在看到完成时停​​止,而不是像它应该在堆顶部那样停止。