A* Nodes bestimmen bei Dynamischer Map
Übersicht

![]() |
Noobody |
![]() Antworten mit Zitat ![]() |
---|---|---|
Vorweg: A* ohne Heuristik ist Dijkstra. Dijkstra in einem Tilebasierten System (d.h. alle Knoten sind in einem regulären Gitter angeordnet) wird auf eine Breitensuche reduziert, was ja auch Midimasters Implementation zu entsprechen scheint (zumindest seiner Beschreibung nach - den Code habe ich noch nicht durchgeschaut).
Ausserdem wage ich mal zu behaupten, dass der Algorithmus für PhilipKs Dwarf Fortress-Klon gedacht ist, man also von einer relativ grossen und offenen Karte ausgehen kann; der Fall eines engen Labyrinths ist also eher selten anzutreffen. @BladeRunner: Midimasters Lösung findet durchaus die kürzeste Lösung - Korrektheit ist ziemlich leicht zu zeigen. Da alle Kanten die gleiche Länge haben (um von einem Tile zum nächsten zu gelangen, benötigt man immer nur einen Schritt), werden im n-ten Schritt der Breitensuche logischerweise auch alle Pfade der Länge n betrachtet. Findet man also in einem Schritt k einen Pfad, der ins Ziel gelangt, hat man logischerweise auch einen der kürzesten Pfade gefunden. Wenn man nämlich die Suche weiterführen würde, würde man nur Pfade der Länge k+1, k+2, .... betrachten, die ja alle länger sind als der bisher gefundene Pfad. Genau hier aber liegt das Problem bei der Breitensuche. Sie betrachtet alle Pfade bis zur Länge des kürzesten Pfades, was im Falle einer eher offenen Karte natürlich phänomenal schlecht ist. Man stelle sich nur mal den Fall vor, dass man einen Zwerg von einem Ende der Karte zum anderen schickt - es wird effektiv jedes einzelne erreichbare Tile überprüft. Tatsächlich also entspricht die Breitensuche einer Art "Floodfill" auf der Karte, die sich solange ausbreitet, bis das Ziel gefunden wurde. In der Tat ist es so, dass A* im pathologisch schlechtesten (und daher sehr seltenen) Fall zur Breitensuche reduziert wird, in der Regel unternimmt A* aber weitaus weniger Schritte als eine Breitensuche. Klar hat man noch leichten Mehraufwand wegen der Heuristik, aber trotzdem ist A* von der Berechnungszeit eher schneller als eine Breitensuche. Das grosse Problem von A* ist aber eher der Speicherverbrauch. Während man sich im A* alle bisher besuchten Knoten merken muss, ist in der Breitensuche nur der Rand des bisher entdeckten Gebietes für weitere Berechnungen nötig. Falls man also tatsächlich Speicherprobleme bekommt, werden andere Lösungen als A* wieder attraktiv, aber im Falle von Performance-Schwierigkeiten wäre A* schon die erste Wahl. Midimaster hat Folgendes geschrieben: Und letzemdlich ist es ja Unsinn einen "noch besseren" Algo zu suchen, wenn für das akt. Problem eine Lösung von unter 1msec gefunden ist.
Das ist grundsätzlich eine wertlose Aussage - Bubblesort läuft auf 10 Elementen auch unter 1ms (und sogar schneller als Quicksort), trotzdem wäre es eine sehr schlechte Idee, Bubblesort für alles zu verwenden ![]() Was ich im Moment noch als grossen Übeltäter sehe, ist die Implementierung des A* - mit Listen ist es schon mal kein Wunder, dass du mehrere Sekunden benötigst ![]() |
||
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun |
![]() |
BladeRunnerModerator |
![]() Antworten mit Zitat ![]() |
---|---|---|
Man verzeihe wenn ich mich missverständlich audrückte, ich meinte mit optimal in dem Fall mit dem günstigsten Laufzeitverhalten. Ich zweifelte nicht an dass Midimasters Breitensuche einen korrekten Weg (und von der Länge optimalen) findet, sondern einfach dass es der effizienteste Weg ist.
Die Gründe für mein Zweifeln hast Du ja in aller Ausführlichkeit dargelegt. |
||
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3 Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64 B3D BMax MaxGUI Stolzer Gewinner des BAC#48, #52 & #92 |
PhillipK |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Huch, hier gehts ja rund.
Grundsätzlich habe ich nichts gegen mehrere Algos um ein Problem zu lösen. Es ist nicht so, das der A* nicht "schnell" ist. Nur wenn eine Breite masse an wegfindungen stattfindet, muss teilweise ewig gewartet werden. Ich habe das heute getestet, ein 256x256 feld angelegt mit ein paar formen, die A* zur so genannten Breitensuche werden lassen, dazu ein paar hundert einheiten hingeklickt. Es hat jeweils zwei-drei sekunden gebraucht, bis eine einheit ihren weg hatte. Um einen Memoryleak vorzubeugen, ist es noch so sortiert, das immer nur ~ 1000 Nodes abgearbeitet werden. Jede einheit die ein anliegen hat, prüft, wieviele nodes schon abgearbeitet wurden. Danach darf sie selbst etwa 150 abarbeiten und subtrahiert die tatsächlich bearbeiteten knoten von der 'genutzt'-variable. Bei 200 einheiten ist es dadurch durchaus verständlich, das sie ziemlich lange stehen. Hierfür suche ich eine art übergeordnetes system - kleine wegweiser. Hat auch den vorteil, das ich die wege an mehreren stellen berechnen lassen kann. Übergeordnet: Einheit fordert weg Wegweiser werden durchforstet, möglicher weg erkundet. (wegweiser wissen, welche wegweise sie mit geringer suchtiefe erreichen können) einheit bekommt liste mit globalen knotenpunkte, über welche er tile-x/y erhalten kann und sucht sie kleine miniwege dazwischen. Vorteil: - Wenig suchtiefe für A* - So gut wie keine chance auf Dead ends / Abstrakte wand-formen, die A* zur Breitensuche zwingen - Einheit kann schon loslaufen, obwohl sie den Tilebasierten weg noch nicht komplett kennt Nachteil: - Ich habe immernoch keinen vollends durchdachten ansatz, WIE - Die "nachbar" könnte auch ein netter CPU aufwand sein - Nochmehr ram verbrauch (aber das ist 2t rangig, solangs unter 10mb bleibt^^) Najut, ich bin grade dabei etwas in der Form umzusetzen. Holzchopf hat mir ebenfalls eine idee zukommen lassen, die ich aber sicher noch 5x studieren muss, bevor ich sie genau verstanden habe - Sehr simpel und dennoch komplex und ram-arm ![]() Eine Typische Spielsituation möchte ich wirklich anpeilen. Ich arbeite solange am Worst-Case (in meiner Testumgebung), bis ich da wirklich das für mich mögliche optimum rausgekitzelt habe. Und der worst-case ist nunmal eine unvorhergesehene Rund-suche durch A* ![]() Allerdings werde ich so oder so für verschiedene Zwecke ein Dijkstra schreiben. Zum einen zb eine geringe umkreissuche - "ist hier irgendwo ein Globaler Wegpunkt, der erreichbar ist?" - das ist ideal. Oder "Such doch bitte um punkt X herum den erstbesten Baum" ----- Zu dem gestern angesprochenen Problem mit meiner neu-implementierung: Ich hatte diverse kleinere Flüchtigkeitsfehler drin, zb "for x2 = x-1 to x2 = x+1". das führte zu einer recht linearen ausbreitung, wie man sich vorstellen kann. Dann gabs ein falsch platziertes Continue bei der "ist in offen" abfrage - nur, wenn ein knoten günstiger war, hatte ich ihn aufgenommen. Heute rennts ganz gut, nicht das schnellste, aber immerhin funktioniert es. Wenn ich morgen früh genug starken kaffe habe, versuche ich mich mal an dem Fibonacci Heap - allerdings grauts mir jetzt schon. Das ist nicht meiner stärke :< Und heute schaue ich nebenbei mal weiter bei den globalen Wegpunkten nach. Wenn dann noch zeit ist, baue ich den wegpunkten einen kleinen zeiger ein - ob sie sich in der nähe von wänden befinden oder ob sie freies feld haben. Fall 1 wäre dann wohl was für A*, fall 2 was für Dijkstra. Sobald ich eins meiner Teilprobleme beseitigt habe, melde ich mich wieder um mich kritisieren zu lassen ![]() Bis dahin gibts den code ![]() BlitzMax: [AUSKLAPPEN] SuperStrict (warnung, nichts für schwach nerven.) Steuerung: Mausrad - SetVirtualResolution, ein Zoom für besseren überblick. Enter -> Zoom reset Enter ausserdem (wären ein AStar algo läuft, gedrückthalten): DrawStep methode wird ausgeführt. Rot = geschlossen, Grün = offen. Linkslick = Unit platzieren MittlereMaustaste = Massen-wegfindung (kleiner test - mehrere objekte zusammen zu einem pfad schicken) Rechtsklick = wand platzieren STRG+Rechtsklick = Luft platzieren :3 |
||
![]() |
Noobody |
![]() Antworten mit Zitat ![]() |
---|---|---|
Also, heute habe ich sowohl einen Fibonacci-Heap als auch A* und eine Breitensuche implementiert und sie auf einem relativ grossen Gebiet (1024x1024) gegeneinander antreten lassen. Um die Prozesse ein wenig besser zu verstehen, werden ausserdem alle betrachteten Wegpunkte eingezeichnet.
Und ich lag falsch: Die Breitensuche ist in fast jedem Fall schneller! Obwohl sie wie erwartet massiv mehr Knoten betrachtet, ist der Aufwand pro Knoten doch um ein vielfaches geringer als beim A*. Grosse Probleme beim A* liegen zum einen bei der komplexen Datenstruktur (Fibonacci-Heap ist leider rechenaufwändiger als ein simples Array und eine Liste bei der Breitensuche) als auch bei der Garbage Collection. Da der BMax GC zirkuläre Referenzen überhaupt nicht mag, diese aber beim Heap als rekursive Datenstruktur extrem oft vorkommen, kommt einiges an Overhead dazu, diese Referenzen zu entfernen. Das macht es leider sehr langsam - ganz ehrlich würde ich hier meinen Speicher lieber selber verwalten. So oder so: Selbst auf einer 1024x1024 Karte mit komplexen Hindernissen und Start/Ende so weit wie möglich entfernt benötigt die Breitensuche nur knapp 200ms - für ein Dwarf-Fortress ähnliches Spiel sollte das also mehr als ausreichend sein (so wie ich DF kenne, wandern die Zwerge ja nur in der nahen Umgebung herum). Der schlimmste Fall, wenn entweder Start oder Zielknoten nicht erreichbar sind, ist aber immer noch sehr ungünstig - in dem Fall laufen beide Algorithmen nämlich zwangsweise die ganze Karte ab, bis sie resignieren. Es wäre also von Vorteil, entweder nach einer vordefinierten Maximal-Pfadlänge abzubrechen, oder bi-directional pathfinding einzubauen, welches sowohl von Ziel- als auch vom Startknoten aus sucht. Falls einer der beiden Pfadsuchen stecken bleibt, kann man schon im Voraus abbrechen. Wie dem auch sei, hier das Programm, mit dem ich die beiden Algorithmen verglichen habe: *klick mich* Und hier noch der Code der Applikation: BlitzMax: [AUSKLAPPEN] SuperStrict Nicht wundern, für den Fibonacci-Heap musste ich eine eigene Listenimplementierung schreiben, da die BMax-interne es überhaupt nicht mag, wenn man selber mit TLinks hantiert (und ausserdem sind gewisse benötigte Operationen wirklich langsam). Fazit: Für eine Tilemap is in BMax ist also wirklich eine Breitensuche zu bevorzugen. In C/++ wäre A* zu prüfen, obwohl er wirklich eher für allgemeine Baumsuchprobleme geeignet scheint als für den simplen Fall einer Tilemap. |
||
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun |
PhillipK |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Du. beeindruckst. mich.
Wow, diese testergebnisse sind wirklich.. Grml. Wiedermal ne woche kopfzerbrechen fürn "hintern". Also echt, der einzige fall, wo dein A* schneller war (5ms vs 165ms) war die direkte sichtverbindung ohne hinderniss.. Gut, das erklärt einiges. Vielleicht sollte man deinen Code (bzw den gesamten post) mal unter Tutorials vermerken? Bin sicher nicht der einzige, der A* unter BMX versuchen wollte. Spart sicher einige frustration :O Ich werde mir morgen mal deinen A* algo mit meinem vergleichen. Ich glaube fast, egal was ich mache, du zeigst mir immer den richtigen weg - von dir lern ich am meisten *g* (Siehe perlin noise.. da bin ich auch halb wahnsinnig geworden ![]() Trotzdem, dankeschön für die arbeit die du darein gesteckt hast. Echt - atemberaubend :O (ps: Sehr schöne Visualisierung! ![]() Edit 16.10.11 - 11:19: Ich zerflüge grade deinen Code ein wenig. Wenn ich ihn dann einigermaßen verstanden habe, werde ich ihn ein wenig verändern - zum einen, schräge schritte zwischen 2 Mauern unterbinden und zum anderen eine Bidirectionale variante einbauen. Vielleichts klappts ja :3 Die testergebnisse sind wirklich atemberaubend. Bei kleineren Mapgrößen ist die Zeit von BFS zwar teilweise höher als die von A*, aber sie ist fast immer konstant. Dh, die A* variante ist fast exponentiell langsamer, je komplexer die map (und so un-eindeutiger die weg, dh dead Ends etc), die BFS variante braucht nur relativ gleichbleibende Zeit. Ich melde mich dann in kürze ![]() |
||
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group