|
| 1 | +package A_Star; |
| 2 | + |
| 3 | +import java.util.*; |
| 4 | + |
| 5 | +public class A_Star { |
| 6 | + |
| 7 | + private static class Graph { |
| 8 | + //Graph's structure can be changed only applying changes to this class. |
| 9 | + private ArrayList<Edge> [] graph; |
| 10 | + |
| 11 | + //Initialise ArrayLists in Constructor |
| 12 | + public Graph(int size) { |
| 13 | + this.graph = new ArrayList[size]; |
| 14 | + for (int i = 0; i < size; i++) { |
| 15 | + this.graph[i] = new ArrayList<>(); |
| 16 | + } |
| 17 | + } |
| 18 | + |
| 19 | + private ArrayList<Edge> getNeighbours(int from) { return this.graph[from]; } |
| 20 | + |
| 21 | + //Graph is bidirectional, for just one direction remove second instruction of this method. |
| 22 | + private void addEdge (Edge edge) { |
| 23 | + this.graph[edge.getFrom()].add(new Edge(edge.getFrom(), edge.getTo(), edge.getWeight())); |
| 24 | + this.graph[edge.getTo()].add(new Edge(edge.getTo(), edge.getFrom(), edge.getWeight())); |
| 25 | + } |
| 26 | + |
| 27 | + private void initializeGraph() { |
| 28 | + this.addEdge(new Edge(0, 19, 75)); this.addEdge(new Edge(0, 15, 140)); |
| 29 | + this.addEdge(new Edge(0, 16, 118)); this.addEdge(new Edge(19, 12, 71)); |
| 30 | + this.addEdge(new Edge(12, 15, 151));this.addEdge(new Edge(16, 9, 111)); |
| 31 | + this.addEdge(new Edge(9, 10, 70)); this.addEdge(new Edge(10, 3, 75)); |
| 32 | + this.addEdge(new Edge(3, 2, 120)); this.addEdge(new Edge(2, 14, 146)); |
| 33 | + this.addEdge(new Edge(2, 13, 138)); this.addEdge(new Edge(2, 6, 115)); |
| 34 | + this.addEdge(new Edge(15, 14, 80)); this.addEdge(new Edge(15, 5, 99)); |
| 35 | + this.addEdge(new Edge(14, 13, 97)); this.addEdge(new Edge(5, 1, 211)); |
| 36 | + this.addEdge(new Edge(13, 1, 101)); this.addEdge(new Edge(6, 1, 160)); |
| 37 | + this.addEdge(new Edge(1, 17, 85)); this.addEdge(new Edge(17, 7, 98)); |
| 38 | + this.addEdge(new Edge(7, 4, 86)); this.addEdge(new Edge(17, 18, 142)); |
| 39 | + this.addEdge(new Edge(18, 8, 92)); this.addEdge(new Edge(8, 11, 87)); |
| 40 | + |
| 41 | + /* |
| 42 | + .x. node |
| 43 | + (y) cost |
| 44 | + - or | or / bidirectional connection |
| 45 | +
|
| 46 | + ( 98)- .7. -(86)- .4. |
| 47 | + | |
| 48 | + ( 85)- .17. -(142)- .18. -(92)- .8. -(87)- .11. |
| 49 | + | |
| 50 | + . 1. -------------------- (160) |
| 51 | + | \ | |
| 52 | + (211) \ .6. |
| 53 | + | \ | |
| 54 | + . 5. (101)-.13. -(138) (115) |
| 55 | + | | | / |
| 56 | + ( 99) ( 97) | / |
| 57 | + | | | / |
| 58 | + .12. -(151)- .15. -(80)- .14. | / |
| 59 | + | | | | / |
| 60 | + ( 71) (140) (146)- .2. -(120) |
| 61 | + | | | |
| 62 | + .19. -( 75)- . 0. .10. -(75)- .3. |
| 63 | + | | |
| 64 | + (118) ( 70) |
| 65 | + | | |
| 66 | + .16. -(111)- .9. |
| 67 | + */ |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + private static class Edge { |
| 72 | + private int from; |
| 73 | + private int to; |
| 74 | + private int weight; |
| 75 | + |
| 76 | + public Edge(int from, int to, int weight) { |
| 77 | + this.from = from; |
| 78 | + this.to = to; |
| 79 | + this.weight = weight; |
| 80 | + } |
| 81 | + |
| 82 | + public int getFrom() { return from; } |
| 83 | + |
| 84 | + public int getTo() { return to; } |
| 85 | + |
| 86 | + public int getWeight() { return weight; } |
| 87 | + } |
| 88 | + |
| 89 | + //class to iterate during the algorithm execution, and also used to return the solution. |
| 90 | + private static class PathAndDistance { |
| 91 | + private int distance; //distance advanced so far. |
| 92 | + private ArrayList<Integer> path; //list of visited nodes in this path. |
| 93 | + private int estimated; //heuristic value associated to the last node od the path (current node). |
| 94 | + |
| 95 | + public PathAndDistance(int distance, ArrayList<Integer> path, int estimated) { |
| 96 | + this.distance = distance; |
| 97 | + this.path = path; |
| 98 | + this.estimated = estimated; |
| 99 | + } |
| 100 | + |
| 101 | + public int getDistance() { return distance; } |
| 102 | + |
| 103 | + public ArrayList<Integer> getPath() { return path; } |
| 104 | + |
| 105 | + public int getEstimated() { return estimated; } |
| 106 | + |
| 107 | + private void printSolution () { |
| 108 | + if (this.path != null) |
| 109 | + System.out.println("Optimal path: " + this.path.toString() + |
| 110 | + ", distance: " + this.distance); |
| 111 | + else |
| 112 | + System.out.println("There is no path available to connect the points"); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + public static void main(String[] args) { |
| 117 | + //heuristic function optimistic values |
| 118 | + int[] heuristic = {366, 0, 160, 242, 161, 178, 77, 151, 226, |
| 119 | + 244, 241, 234, 380, 98, 193, 253, 329, 80, 199, 374}; |
| 120 | + |
| 121 | + Graph graph = new Graph(20); |
| 122 | + graph.initializeGraph(); |
| 123 | + |
| 124 | + PathAndDistance solution = aStar(3, 1, graph, heuristic); |
| 125 | + solution.printSolution(); |
| 126 | + |
| 127 | + } |
| 128 | + |
| 129 | + public static PathAndDistance aStar(int from, int to, Graph graph, int[] heuristic) { |
| 130 | + //nodes are prioritised by the less value of the current distance of their paths, and the estimated value |
| 131 | + //given by the heuristic function to reach the destination point from the current point. |
| 132 | + PriorityQueue<PathAndDistance> queue = new PriorityQueue<> |
| 133 | + (Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated()))); |
| 134 | + |
| 135 | + //dummy data to start the algorithm from the beginning point |
| 136 | + queue.add(new PathAndDistance(0, new ArrayList<>(Arrays.asList(from)), 0)); |
| 137 | + |
| 138 | + boolean solutionFound = false; |
| 139 | + PathAndDistance currentData = new PathAndDistance(-1, null, -1); |
| 140 | + while (!queue.isEmpty() && !solutionFound) { |
| 141 | + currentData = queue.poll(); //first in the queue, best node so keep exploring. |
| 142 | + int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); //current node. |
| 143 | + if (currentPosition == to) |
| 144 | + solutionFound = true; |
| 145 | + else |
| 146 | + for (Edge edge : graph.getNeighbours(currentPosition)) |
| 147 | + if (!currentData.getPath().contains(edge.getTo())) { //Avoid Cycles |
| 148 | + ArrayList<Integer> updatedPath = new ArrayList<>(currentData.getPath()); |
| 149 | + updatedPath.add(edge.getTo()); //Add the new node to the path, update the distance, |
| 150 | + // and the heuristic function value associated to that path. |
| 151 | + queue.add(new PathAndDistance(currentData.getDistance() + edge.getWeight(), |
| 152 | + updatedPath, heuristic[edge.getTo()])); |
| 153 | + } |
| 154 | + } |
| 155 | + return (solutionFound) ? currentData : new PathAndDistance(-1, null, -1); |
| 156 | + //Out of while loop, if there is a solution, the current Data stores the optimal path, and its distance |
| 157 | + } |
| 158 | +} |
0 commit comments