Stufe 4: Suchen einer Lösung mit Backtracking
Stufe 4: Suchen einer Lösung mit BacktrackingKlasse BacktrackSuche
Diese Klasse wird aus BacktrackIO spezialiert. Sie nutzt die gesamte Infrastruktur. Sie überschreibt
- loesungFinden()
- main(String[] args): Sie müssen hier eine Instanz von BacktrackSuche anlegen und anzeigen
Klasse Ariadne
In dieser Klasse wird die Suche implementiert.
Zuschauen beim Suchen...
Implementieren Sie eine Wartebedingung, dann können Sie Ihrem Algorithmus zuschauen.
Definieren Sie eine Variable mit Millisekunden Schlafzeit
public static final int WARTEN=10; //ms Schlafen vor dem nächsten Schritt
Lassen Sie den aktuellen Thread schlafen
public static final int WARTEN=1000; // ms Schlafen vor dem nächsten Schritt try { Thread.sleep(WARTEN); } catch (InterruptedException ex) { System.out.println("Probleme mit Thread aufwachen"); }
Das GUI muss jetzt noch wissen, dass sich etwas geändert hat. Deshalb benötigt man einen Zeiger auf das GUI. Das schafft eine Abhängigkeit vom GUI und ist nicht schön (muss aber sein).
Triggern Sie ein Update indem Sie das Labyrinth des GUIs erneuern und dann alle Buttons dazu zwingen den Zustand ihrer Zelle zu checken:
bt.laby.update(laby); bt.updateButtons();
Die rekursive Suche
Es gibt einen Einstieg in die Suche ohne Parameter mit der Methode suche(). Die rekursive Suche findet in einer überschriebenen Methode statt, die die Teilstrecke von einem Zwischenstart zu einem Zwischenziel sucht.
Alle Methoden arbeiten auf dem gleichen Labyrinth, dadurch kann man Stellen entdecken auf denen man schon mal war.
Eine separate Methode findeOptionen() checkt alle Nachbarpositionen und gibt eine Liste von Optionen zurück.
Optionen sind freie Felder. Keine Optionen sind die Wände und Felder die schon einmal betreten wurden.
UML
Musterlösung
Github Projekt scalingbits/dhbwjava
- Klasse Position.java
- Klasse Zelle.java
- Klasse Labyrinth.java
- Klasse Backtrack.java
- Klasse BacktrackIO.java
- Klasse BacktrackSuche.java
Hilfe! Wie packe ich das an?Grundlegende Überlegungen
|
- Erzeugen Sie eine spezialisierte Klasse BacktrackSuche aus der Klass BacktrackIO
- Kopieren Sie sich das Hauptprogramm der Oberklasse
- Anpassung: Erzeugen Sie ein Objekt der aktuellen Klasse
- Testen: Läuft alles wie zuvor?
- Überschreiben Sie die Methode loesungFinden()
- Implementieren Sie einen ActionListener der die Suche started
- Setzen Sie das Statusfeld. Das ist ein guter Test ob der Aktionlistener funktioniert.
- Starten Sie die Suche in einem eigenen Thread, dann kann das GUI parallel arbeiten und Sie können die Suche beobachten
- Implementieren Sie die Klasse Ariadne
- Sie können Sie auch Gretel nennen, Ariadne streut in meinem Beispiel Krümel...
- Variablen
- int zum Warten in Millisekunden, Ariadne düst sonst wie von der Hummel gestochen durch das Labyrinth und Sie können nicht sehen wie der Weg gefunden wird.
- Ein Zeiger auf ein Labyrinth: Die Klasse arbeitet rekursiv auf einem eigenen Labyrinth
- Ein Rückzeiger auf Backtrack: Hiermit können Sie das GUI nach jedem gestreuten Krümel um ein Update bitten. Dann können Sie Ariadne beim Krümelstreuen zuschauen. Das ist nicht elegant, weil damit Ariadne von dem GUI abhängt. Das ganze am besten protected, diese Klasse wird nochmals spezialisiert werden
- Einen Konstruktor
- Parameter: Das GUI (Javaklasse Backtrack) welches den Suchauftrag gibt. Man kann dann nach jedem Krümel ein Update machen und zuschauen
- Legen Sie sich ein neues Labyrinth an. Kopieren Sie sich das Labyrinth des GUIs!
- Hilfsmethode: findeOptionen() implementieren
- Geben Sie ein Set mit allen Optionen zurück.
- Testen Sie alle Punkte um den gegebenen Startpunkt. Vorsicht; Ihr Feld ist endlich, es kann Feldüberläufe geben.
- Tipp: Benutzen Sie die Methode Position.neuerWeg(), sie wird wahr wenn das entsprechende Feld keine Wand oder kein Krümel ist.
- Eine Methode suche()
- Diese Methode wird vom GUI aufgerufen
- Rückgabe: Eine Liste von Position mit dem Pfad zur Lösung
- Diese Methode nutzt den hardcodierten Start und Ziel des Labyrinths zum Aufruf der rekursiven Methode suche()
- Testen der Rekursion in Teilschrittem ist schwierig. Möglichkeiten sind:
- findeOptionen() mit Testfällen testen
- Abbruch der Rekursion testen: Start ist gleich Ziel
- Design der Rekursion...
- Diese Methode bekommt einen individuellen Start und ein individuelles Ziel. Diese Methode soll Teilaufgaben lösen!
- Streue erstmal einen Krümel. Gretel meint das ist immer eine gute Sache...
- Das ist die echte Arbeit zum Verkleinern unseres Problems bei der Rekursion
- Tue gutes und sprich darüber...
- Kopiere das Suchlabyrinth um in das GUI-Labyrinth (es hat jetzt einen neuen Krümel=
- Rufe in Backtrack die Methode updateButtons() auf
- So kann man sehen was gerade passiert
- Diese Methode ruft eine Methode findeOptionen() auf
- Diese Methode liefert eine Liste von Optionen
- Optionen sind freie Felder: keine Wand, kein Krümel
- Liegt da ein Krümel, so waren wir schon mal da und haben den Weg gefunden (super) oder auch nicht (da müssen wir nicht weitersuchen)
- Ergebnis ist eine Menge von Positionen
- Die Rekursion:
- Arbeite alle Optionen ab
- Erstmal Pause...
- Lasse den Thread etwas schlafen, damit man das Herumirren im Labyrinth verfolgen kann.
- Hänsel möchte wissen ob wir schon da sind?
- Wenn der Start das Ziel ist haben wir die Lösung gefunden
- wir legen eine neue Liste an
- falls nicht rufen wir die Methode suche() rekursiv auf.
- Verwende die Option als Start
- Verwende das ursprüngliche Ziel
- Wir hängen die aktuelle Position als Lösung ein und
- und geben sie zurück
- Ende der Methode
- Wenn der Start das Ziel ist haben wir die Lösung gefunden
- Merke dir die
- erste Liste (Heureka!)
- die kürzeste Liste (Hänsel läuft nicht gerne)
- Nach dem Abarbeiten aller Optionen:
- Nimm Lösungsliste (der Weg)
- Hänge vorne die Option and um den Lösungsweg zu verlängern
- Gib die Liste mit der gewünschten Lösung zurück
- Gibt es keine Lösung ist die Liste leer
- Die Länge der Liste ist die Länge des Pfades
- 1628 views