Iterative Deepening A Star in Java

Gepostet: , Zuletzt aktualisiert:

package iterative_deepening_a_star.java.simple;

/**
 * Used to perform the Iterative Deepening A* Algorithm to find the shortest path from a start to a target node.
 */
public class IterativeDeepeningAStar {
    /**
     * Performs iterative deepening A Star (A*).
     * Can be modified to handle graphs by keeping track of already visited nodes.
     *
     * @param tree      An adjacency-matrix-representation of the tree where (x,y) is the weight of the edge or 0 if there is no edge.
     * @param heuristic An estimation of distance from node x to y that is guaranteed to be lower than the actual distance. E.g. straight-line distance.
     * @param start      The node to start from.
     * @param goal      The node we're searching for.
     * @return The shortest distance to the goal node. Can be easily modified to return the path.
     */
    public static double iterativeDeepeningAStar(int[][] tree, double[][] heuristic, int start, int goal) {
        double threshold = heuristic[start][goal];
        while (true) {
            System.out.printf("Iteration with threshold: %.2f\n", threshold);
            double distance = iterativeDeepeningAStar(tree, heuristic, start, goal, 0, threshold);
            if (distance == Double.MAX_VALUE) {
                // Node not found and no more nodes to visit
                return -1;
            } else if (distance < 0) {
                // if we found the node, the function returns the negative distance
                System.out.println("Found the node we're looking for!");
                return -distance;
            } else {
                // if it hasn't found the node, it returns the (positive) next-bigger threshold
                threshold = distance;
            }
        }
    }

    /**
     * Performs DFS up to a depth where a threshold is reached (as opposed to interative-deepening DFS which stops at a fixed depth).
     * Can be modified to handle graphs by keeping track of already visited nodes.
     *
     * @param tree      An adjacency-matrix-representation of the tree where (x,y) is the weight of the edge or 0 if there is no edge.
     * @param heuristic An estimation of distance from node x to y that is guaranteed to be lower than the actual distance. E.g. straight-line distance.
     * @param node      The node to continue from.
     * @param goal      The node we're searching for.
     * @param distance  Distance from start node to current node.
     * @param threshold Until which distance to search in this iteration.
     * @return The shortest distance to the goal node. Can be easily modified to return the path.
     */
    private static double iterativeDeepeningAStar(int[][] tree, double[][] heuristic, int node, int goal, double distance, double threshold) {
        System.out.println("Visiting Node " + node);

        if (node == goal) {
            // We have found the goal node we we're searching for
            return -distance;
        }

        double estimate = distance + heuristic[node][goal];
        if (estimate > threshold) {
            System.out.printf("Breached threshold with heuristic: %.2f\n", estimate);
            return estimate;
        }

        //...then, for all neighboring nodes....
        double min = Double.MAX_VALUE;
        for (int i = 0; i < tree[node].length; i++) {
            if (tree[node][i] != 0) {
                double t = iterativeDeepeningAStar(tree, heuristic, i, goal, distance + tree[node][i], threshold);
                if (t < 0) {
                    // Node found
                    return t;
                } else if (t < min) {
                    min = t;
                }
            }
        }
        return min;
    }
}

Über den Algorithmus und die Programmiersprache in diesem Snippet:

Iterative Deepening A Star Algorithmus

Der A-Star Algorithmus mit iterativer Vertiefung (IDA & ast;) ist ein Algorithmus, der verwendet wird, um das Problem des kürzesten Pfades in einem Baum zu lösen, kann jedoch modifiziert werden, um Graphen (d. H. Zyklen) zu handhaben. Es baut auf der ID-DFS (Iterative Deepening Depth-First Search) auf, indem eine Heuristik hinzugefügt wird, um nur relevante Knoten zu untersuchen.

Beschreibung des Algorithmus

Während Iterative Deepening DFS eine einfache Tiefe verwendet, um zu entscheiden, wann die aktuelle Iteration abgebrochen und mit einer höheren Tiefe fortgesetzt werden soll, Iterative Vertiefung Ein Stern verwendet eine Heuristik, um zu bestimmen, welche Knoten erforscht und in welcher Tiefe gestoppt werden soll. Dies ähnelt der Art und Weise, wie Dijkstra den Knoten immer mit dem derzeit kürzesten Unterschied untersucht und A Star eine Heuristik hinzufügt, um nur Knoten zu untersuchen, die tatsächlich näher am Ziel liegen.

Im Einzelnen führt dies zu den folgenden Schritten:

  1. Für jedes Kind des aktuellen Knotens
  2. Wenn es sich um den Zielknoten handelt, kehren Sie zurück
  3. Wenn der Abstand plus die Heuristik den aktuellen Schwellenwert überschreitet, geben Sie diesen Schwellenwert zurück
  4. Setzen Sie den aktuellen Knoten auf diesen Knoten und kehren Sie zu 1 zurück.
  5. Nachdem Sie alle Kinder durchlaufen haben, gehen Sie zum nächsten Kind des Elternteils (dem nächsten Geschwister).
  6. Nachdem Sie alle untergeordneten Elemente des Startknotens durchlaufen haben, erhöhen Sie den Schwellenwert auf den kleinsten der überschrittenen Schwellenwerte.
  7. Wenn wir alle Blattknoten (unten) erreicht haben, existiert der Zielknoten nicht.

Beispiel des Algorithmus

Betrachten Sie das folgende Diagramm: Grafik für den Iterative Deepening A Star (IDA *) -Algorithmus für kürzeste Wege

Die Schritte, die der Algorithmus in diesem Diagramm ausführt, wenn Knoten 0 als Startpunkt und Knoten 6 als Ziel in der angegebenen Reihenfolge angegeben werden, sind:

  1. Iteration mit Schwelle: 6.32
  2. Besuche Knoten 0
  3. Besuche Knoten 1
  4. Schwelle mit Heuristik überschritten: 8.66
  5. Besuche Knoten 2
  6. Schwelle mit Heuristik überschritten: 7.00
  7. Iteration mit Schwelle: 7.00
  8. Besuche Knoten 0
  9. Besuche Knoten 1
  10. Schwelle mit Heuristik überschritten: 8.66
  11. Besuche Knoten 2
  12. Besuche Knoten 5
  13. Schwelle mit Heuristik überschritten: 8.83
  14. Iteration mit Schwelle: 8.66
  15. Besuche Knoten 0
  16. Besuche Knoten 1
  17. Besuche Knoten 3
  18. Schwelle mit Heuristik überschritten: 12.32
  19. Besuche Knoten 4
  20. Schwelle mit Heuristik überschritten: 8.83
  21. Besuche Knoten 2
  22. Besuche Knoten 5
  23. Schwelle mit Heuristik überschritten: 8.83
  24. Iteration mit Schwelle: 8,83
  25. Besuche Knoten 0
  26. Besuche Knoten 1
  27. Besuche Knoten 3
  28. Schwelle mit Heuristik überschritten: 12.32
  29. Besuche Knoten 4
  30. Besuche Knoten 2
  31. Besuche Knoten 5
  32. Besuche Knoten 6
  33. Den Knoten gefunden, den wir suchen!

Endgültig niedrigster Abstand von Knoten 0 zu Knoten 6: 9

Beachten Sie, dass der Algorithmus in der Iteration, in der er den Zielknoten gefunden hat, nicht weiter von Knoten 3 aus nach unten gesucht hat. Wenn Knoten 3 Kinder gehabt hätte, während Iterative Deepening DFS sie möglicherweise (und unnötig!) Erkundet hätte, hätte Iterative Deepening A Star dies nicht getan.

Laufzeit des Algorithmus

Die Laufzeitkomplexität von Iterative Deepening A Star entspricht im Prinzip der von Iterative Deepening DFS. In der Praxis können jedoch viele der Pfade eliminiert werden, bevor wir untersucht werden, wenn wir eine gute Heuristik wählen, was zu einer signifikanten Zeitverbesserung führt. Weitere Informationen darüber, wie die Heuristik die Komplexität beeinflusst, finden Sie im Wikipedia-Artikel.

Speicherkomplexität des Algorithmus

Die räumliche Komplexität von Iterative Deepening A Star ist die Speichermenge, die für den Baum oder das Diagramm benötigt wird. O(| N|), |N| = Anzahl der Knoten im Baum oder Diagramm, die für Bäume durch b^d ersetzt werden können, wobei b der Verzweigungsfaktor und d ist die Tiefe. Unabhängig davon, welchen Platz die Heuristik benötigt.

Java

The Java Logo

Java ™ ist eine kompilierte Sprache, die für viele Zwecke verwendet wird, von eingebetteten Systemen über UI-Anwendungen bis hin zu Webservern.

“Hello World” in Java

Das Wichtigste zuerst - hier erfahren Sie, wie Sie Ihre erste Codezeile in Java ausführen können.

  1. Laden Sie die neueste Version von Java von [java.com] herunter und installieren Sie sie (https://www.java.com/en/download/). Sie können auch eine frühere Version herunterladen, wenn Ihr Anwendungsfall dies erfordert.
  2. Öffnen Sie ein Terminal, stellen Sie sicher, dass die Befehle “javac” und “javac” funktionieren und dass sich der Befehl, den Sie verwenden werden, auf die Version bezieht, die Sie gerade installiert haben, indem Sie “java -version” ausführen. Wenn der Fehler “Befehl nicht gefunden” (oder ähnlich) angezeigt wird, starten Sie die Befehlszeile und, falls dies nicht hilft, Ihren Computer neu. Wenn das Problem weiterhin besteht, finden Sie hier einige hilfreiche Fragen zu StackOverflow für jede Plattform:

  3. Sobald dies funktioniert, kopieren Sie das folgende Snippet in eine Datei mit dem Namen HelloWorld.java:

    class HelloWorld {
    public static void main(String[] args) {
        // Paste any following code snippets here.
        System.out.println("Hello World");
    }
    }
  4. Ändern Sie das Verzeichnis, indem Sie “cd path / to / HelloWorld” eingeben, dann “javac HelloWorld.java” ausführen, um die Datei zu kompilieren (die den Bytecode erstellt), und dann “java HelloWorld” ausführen (* ohne die .java-Endung *). Dies sollte “Hello World” auf Ihrem Terminal drucken.

Das ist es! Beachten Sie, dass die Eintrittsbarriere bei Java etwas höher ist als bei z. Python - aber Java ist viel schneller und weist meiner Erfahrung nach aufgrund starker Typisierung und anderer Faktoren weniger Fehler in großen Projekten auf.

Fundamentals in Java

Um in Java implementierte Algorithmen und Technologien zu verstehen, muss man zunächst verstehen, wie grundlegende Programmierkonzepte in dieser bestimmten Sprache aussehen. Jedes der folgenden Snippets sollte vom Boilerplate-Code des Hello World-Beispiels umgeben sein und mit den oben genannten Befehlen kompiliert und ausgeführt werden.

Variables and Arithmetic

Variablen in Java sind statisch typisiert, dh der Inhalt einer Variablen muss beim Schreiben des Codes angegeben werden. Der Datentyp für ganze Zahlen ist beispielsweise “int”. Zahlen mit Dezimalstellen werden je nach erforderlicher Genauigkeit mit “float” oder “double” eingegeben. Der Texttyp lautet “String”.

int number = 5;
double decimalNumber = 3.25;
double result = number * decimalNumber;
String callout = "The number is ";
// In this instance, the values are concatenated rather than added because one of them is a String.
System.out.println(callout + result);

Arrays

Arrays in Java sind echte Arrays (im Gegensatz zu beispielsweise Python, wo sie als Listen implementiert sind). Dies hat zur Folge, dass die Größe beim Erstellen festgelegt werden muss und nicht geändert werden kann, aber auch, dass sie in Java effizienter sind als in Python.

int[] integers = new int[5];
integers[3] = 12; // Assigning values to positions in the array
// integers[4] is 0, integers[6] would give IndexOutOfBoundsException
String[] strings = {"Hello", "World"}; // Array initialization with initial values
System.out.println(strings[0] + integers[3]); // Prints "Hello12"

Conditions

Wie die meisten Programmiersprachen kann Java “if-else” -Anweisungen ausführen. Darüber hinaus kann Java auch “switch-case” -Anweisungen ausführen.

int value = 5;
if(value == 5){
    System.out.println("Value is 5");
} else if(value < 5){
    System.out.println("Value is less than 5");
} else {
    System.out.println("Value is something else");
}

switch (value){
    case 1:
        System.out.println("Value is 1");
        break; // Don't go further down the cases
    case 2:
        System.out.println("Value is 2");
        break; // Don't go further down the cases
    case 3:
        System.out.println("Value is 3");
        break; // Don't go further down the cases
    case 4:
        System.out.println("Value is 4");
        break; // Don't go further down the cases
    case 5:
        System.out.println("Value is 5");
        break; // Don't go further down the cases
    default:
        System.out.println("Value is something else");
}

Der obige Java-Code gibt zweimal “Wert ist 5” aus.

Schleifen

Java unterstützt “for” -, “while” - und “do while” -Schleifen. Die Anweisungen break undcontinue werden ebenfalls unterstützt. Das folgende Beispiel zeigt die Unterschiede:

int value = 2;
for (int i = 0; i < value; i++) {
    System.out.println(i);
}
while (value > 0) {
    System.out.println(value);
    value--;
}
do {
    System.out.println(value);
    value--;
} while (value > 0);

Dadurch wird Folgendes auf das Terminal gedruckt:

0
1
2
1
0

Beachten Sie die letzte “0”: Sie wird gedruckt, weil in der “do-while” -Schleife im Vergleich zur “while” -Schleife. Der Codeblock wird mindestens einmal ausgeführt, bevor die Bedingung überprüft wird.

Funktionen

Funktionen in Java können Teil einer Klasse oder eines Objekts einer Klasse sein. Für weitere Informationen zur objektorientierten Programmierung empfehle ich den w3schools-Kurs. Hier ist ein minimales Beispiel für eine Funktion als Teil einer Klasse (auch als statische Funktion bezeichnet):

class HelloWorld {
    public static void main(String[] args) {
        System.out.println(addNumbers(3, 4));
    }

    public static int addNumbers(int numberOne, int numberTwo) {
        return numberOne + numberTwo;
    }
}

Und hier ist ein Beispiel für den Aufruf einer Funktion eines Objekts einer Klasse:

class HelloWorld {
    public static void main(String[] args) {
        System.out.println(new HelloWorld().addNumbers(3, 4));
    }

    public int addNumbers(int numberOne, int numberTwo) {
        return numberOne + numberTwo;
    }
}

Beachten Sie, wie im ersten Beispiel das Schlüsselwort static verwendet wird und im zweiten Beispiel das Objekt der Klasse instanziiert werden muss, bevor in die Funktion dieses Objekts aufrufen kann. Dies sind einige der Unterschiede bei Klassenmethoden und Objektfunktionen.

Syntax

Java erfordert die Verwendung von geschweiften Klammern ({}), um Codeblöcke in Bedingungen, Schleifen, Funktionen usw.; Außerdem sind am Ende der Anweisungen Semikolons erforderlich. Dies kann zwar zu lästigen Syntaxfehlern führen, bedeutet jedoch auch, dass die Verwendung von Leerzeichen für die bevorzugte Formatierung (z. B. Einrücken von Codeteilen) den Code nicht beeinflusst.

Fortgeschrittenes Wissen in Java

Java wurde erstmals 1995 veröffentlicht und ist ein Multi-Paradigma. Das heißt, es ist in erster Linie objektorientiert, hat aber auch funktionale und reflektierende Elemente. Es ist statisch typisiert, bietet jedoch in neueren Versionen ein gewisses Maß an dynamischer Typisierung. Für weitere Informationen hat Java einen großartigen Artikel Wikipedia.