Ich muss ein Programm schreiben, das das Labyrinth löst. Das Labyrinth hat eine Diagrammstruktur, bei der jeder Knoten - ein Raum und Kanten - in andere Räume austritt:
Spezifikation:
- Wir beginnen in einem zufälligen Raum.
- Labyrinth hat Sackgassen, 0 oder wenige Ausgänge.
- Wir wissen nichts über alles Labyrinth, nur die Anzahl der aktuellen Räume und die Liste der Türen.
- Wenn wir einen neuen Raum betreten, wissen wir nicht, woher wir kommen (welche Tür vom aktuellen Raum führt uns zurück zum vorherigen Raum).
- Wir können erkennen, wann wir den Ausgang erreichen.
- Bei jedem Schritt bewegen wir uns vom aktuellen Raum zu einer verfügbaren Tür.
- Wenn wir zum Beispiel in Raum 6 sind, können wir nicht einfach springen, um zu beginnen. Es ist wie eine Roboterbewegung.
- Wir kennen die ID des aktuellen Zimmers genau. Wir kennen den Ausweis jeder Tür im aktuellen Raum (nicht in jedem Labyrinth).
- Wir steuern Roboter.
Ich habe alle bekannten Algorithmen untersucht, aber alle erfordern zumindest zusätzliche Fähigkeiten, um zum vorherigen Raum zurückzukehren. Laut Spezifikation können wir keine Algorithmen verwenden, die nach dem kürzesten Weg suchen (eigentlich brauche ich den kürzesten nicht), da wir nur über den aktuellen Raum Bescheid wissen. Wir können keine Links- (Rechts-) Folgealgorithmen verwenden, da wir die Richtung der Ausgänge nicht kennen. Wahrscheinlich besteht die einzige Lösung, die den Spezifikationen entspricht, darin, in jedem Raum einen zufälligen Ausgang zu wählen, in der Hoffnung, dass ein zeitlicher Ausgang gefunden wird ...
Irgendwelche Vorschläge, wie man ein solches Labyrinth klüger lösen kann?
Antworten:
Hmm, Sie kennen die Nummer des tatsächlichen Raums. So können Sie eine Datenstruktur erstellen. Ich denke, die Liste der Ausgänge enthält nicht die Zimmernummern. Aber nachdem Sie zufällig eine ausgewählt haben, wissen Sie zumindest, dass es eine Verbindung gibt.
Angenommen, Sie befinden sich in Raum 4 und wählen einen von drei zufälligen Ausgängen. Jetzt teilt Ihnen das System mit, dass Sie sich in Raum 6 befinden und nur noch einen Ausgang haben. Sie wählen das und sind wieder in Raum 4. An diesem Punkt haben Sie einige Informationen über die Struktur des Labyrinths gesammelt. Sie wählen nun einen anderen Ausgang und enden in Raum 5. Weitere Informationen zu Raum 4 (ein Ausgang zu 6, ein Ausgang zu 5)
Können Sie einen bestimmten Ausgang wählen? Sind sie nummeriert, sagen wir, wenn Sie in Raum 4 den Ausgang 1 wählen, enden Sie immer in 6? Ansonsten können Sie sich zumindest über mögliche Routen informieren.
Ok, lies einfach deinen Kommentar. Wenn die Ausgänge IDs haben und diese statisch mit einem Raum verknüpft sind (auch wenn Sie zunächst nicht wissen, welcher), wählen Sie einfach einen neuen aus und merken Sie sich die Ausgangs- / Raumverbindungen und merken Sie sich, welcher Ausgang bereits versucht wurde. Versuchen Sie es mit nicht erprobten Ausgängen, bis Sie jeden einzelnen Raum durchsucht haben.
Auf diese Weise ist es eigentlich einfach. Nach einigen Schritten sollten Sie eine mehr oder weniger vollständige Liste der Verbindungen haben. Nur wenn Sie einen neuen Raum betreten, können Sie (für ein paar Schritte) zufällig herumlaufen. Aber mit jedem Schritt erhalten Sie mehr Informationen und wenn Sie einen zuvor besuchten Raum betreten, können Sie eine klügere Entscheidung treffen (Sie können den Sackgassenraum 6 nicht erneut testen, wenn Sie beispielsweise auf 4 zurückfinden, da er keine nicht getesteten Ausgänge hat).
Bearbeiten Die Idee ist, zuerst zufällige Schritte auszuführen und die Informationen, die Sie finden, wie beschrieben zu protokollieren (Dans Beschreibung ist prägnanter). Wenn Sie sich in einem Raum ohne unbekannte Ausgänge befinden, können Sie mit jedem bekannten Pfadfinder den kürzesten Weg zu einem Raum mit Ausgängen finden, die Sie noch nicht erkundet haben.
Nicht 100% sicher, aber ich denke, Peter Norvig hat in seinem Buch "Künstliche Intelligenz: Ein moderner Ansatz" über ein ähnliches Problem (Das Wumpus-Labyrinth) geschrieben. Wenn ich mich recht erinnere, ging es weniger um die Wegfindung als vielmehr um die Entscheidungsfindung in Bezug auf einige Informationen, die das System über benachbarte Räume erhalten konnte.
Bearbeiten:
Nein, es ist nicht nur zufällig. Indem Sie bereits versuchte Exits verfolgen, entfernen Sie unnötige Redundanz. Eigentlich müssen Sie nicht einmal zufällig auswählen.
Angenommen, Sie beginnen in Raum 4. Informationen erhalten Sie: 3 Ausgänge A, B, C Sie wählen immer den ersten, noch nicht verwendeten Ausgang. Beginnen Sie also mit A. Sie enden in Raum 6. Jetzt erinnern Sie sich an 4A => 6 (und verwendet) in Raum 6 und erhalten die Info 1 Ausgang A. Wieder wählen Sie den ersten unbenutzten (und in diesem Fall nur Ausgang) Zurück in Raum um 6A zu kennen => 4 (und keine weiteren Ausgänge in Raum 6) Nun wählen Sie den nächsten Ausgang B und erreichen Raum 5 ...
Früher oder später haben Sie alle Räume auf diese Weise durchsucht. Aber in einer systematischen Angelegenheit.
Der einzige Grund für die Suche nach einem Pfad ist, wenn Sie sich in einem Raum befinden, in dem bereits alle Ausgänge erkundet sind. Dann möchten Sie einen direkten Weg zum nächsten Raum mit unerforschten Ausgängen finden, um sich Ihrer Suche zu widmen. Der Haupttrick besteht also weniger darin, zu wissen, welcher Ausgang zu welchem Raum führt (obwohl dies hilfreich sein kann), sondern darin, noch nicht erprobte Ausgänge im Auge zu behalten.
Auf diese Weise können Sie beispielsweise vermeiden, ständig im Kreis zu laufen, was ein Risiko für einen rein zufälligen Ansatz darstellen würde.
In Ihrem Beispiellabyrinth würde dies höchstwahrscheinlich nicht viel ausmachen. Aber in einem großen Labyrinth mit vielen Räumen und Verbindungen und möglicherweise kniffligen, kreisförmig angeordneten Räumen garantiert dieses System, dass Sie den Ausgang mit so wenig Versuchen wie möglich finden.
(Ich denke, das ist wahrscheinlich das gleiche System, das Byte56 in Gamers entwickelt hat.)
quelle
Ich erkenne, dass Sie wahrscheinlich das Wesentliche aus anderen Antworten erhalten haben, aber es war eine lustige Frage und ich hatte Lust, ein wenig Python-Codierung zu machen. Dies ist mein objektorientierter Ansatz. Einrückung definiert den Umfang.
Diagrammdarstellung
Das Diagramm kann einfach als Schlüssel- und Wertewörterbuch gespeichert werden, wobei der Schlüssel die Raum-ID und der Wert ein Array der Räume ist, zu denen er führt.
Agentenschnittstelle
Zuerst sollten wir uns überlegen, welche Informationen der Agent aus der Umgebung lernen kann und welche Vorgänge er ausführen kann. Dies vereinfacht das Nachdenken über den Algorithmus.
In diesem Fall sollte der Agent in der Lage sein, die Umgebung nach der ID des Raums abzufragen, in dem er sich befindet. Er sollte in der Lage sein, die Anzahl der Türen in dem Raum zu ermitteln, in dem er sich befindet ( beachten Sie, dass dies nicht die ID der Räume ist, in denen er sich befindet Türen führen zu! ) und er sollte sich durch Angabe eines Türindex durch eine Tür bewegen können. Alles andere, was ein Agent weiß, muss vom Agenten selbst herausgefunden werden.
Agentenwissen
Wenn der Agent die Karte zum ersten Mal betritt, kennt er nur die Anzahl der Türen im Raum und die ID des Raums, in dem er sich gerade befindet. Ich musste eine Struktur erstellen, in der Informationen gespeichert werden, die der Agent gelernt hat, z. B. welche Türen er nicht war durch, und wohin die Türen dazu führen, war durch gewesen.
Diese Klasse repräsentiert die Informationen zu einem einzelnen Raum. Ich habe mich dafür entschieden, die nicht besuchten Türen als
set
und die besuchten Türen als zu speicherndictionary
, wobei der Schlüssel die Tür-ID und der Wert die ID des Raums ist, zu dem er führt.Agentenalgorithmus
Jedes Mal, wenn der Agent einen Raum betritt, durchsucht er sein Wissenswörterbuch nach Informationen über den Raum. Wenn für diesen Raum keine Einträge vorhanden sind, wird ein neuer erstellt
RoomKnowledge
und dieser dem Wissenswörterbuch hinzugefügt.Es wird geprüft, ob der aktuelle Raum der Zielraum ist. Wenn ja, wird es zurückgegeben.
Wenn es Türen in diesem Raum gibt, die wir nicht besucht haben, gehen wir durch die Tür und lagern, wohin sie führen. Wir setzen dann die Schleife fort.
Wenn es keine nicht besuchten Türen gab, gehen wir zurück durch die Räume, die wir besucht haben, um eine mit nicht besuchten Türen zu finden.
Die
Agent
Klasse erbt von derAgentInterface
Klasse.Unterstützende Funktionen
Ich musste eine Funktion schreiben, die einen Schlüssel in einem Wörterbuch mit einem bestimmten Wert findet, da wir beim Zurückverfolgen die ID des Raums kennen, zu dem wir gelangen möchten, aber nicht, welche Tür wir verwenden müssen, um dorthin zu gelangen.
Testen
Ich habe alle Kombinationen der Start- / Endposition in der oben angegebenen Karte getestet. Für jede Kombination werden die besuchten Räume ausgedruckt.
Anmerkungen
Das Backtracking ist nicht sehr effizient - im schlimmsten Fall könnte es durch jeden Raum gehen, um zu einem angrenzenden Raum zu gelangen, aber das Backtracking ist ziemlich selten - in den obigen Tests wird es nur dreimal zurückverfolgt. Ich habe es vermieden, Ausnahmebehandlungen einzufügen, um den Code kurz zu halten. Kommentare zu meinem Python sind willkommen :)
quelle
Im Wesentlichen haben Sie ein Richtungsdiagramm, in dem jeder verbundene Raum durch zwei unbekannte Passagen verbunden ist - eine in beide Richtungen. Angenommen, Sie beginnen im Knoten
1
und in den TürenA
undB
führen hinaus. Sie wissen nicht, was sich hinter jeder Tür befindet, also wählen Sie einfach die Tür ausA
. Sie erhalten auf Raum2
, die Türen hatC
,D
undE
. Sie wissen jetzt, dass die TürA
von Raum1
zu Raum führt2
, aber Sie wissen nicht, wie Sie zurückkommen sollen, also wählen Sie zufällig die Tür ausC
. Du kommst zurück ins Zimmer1
! Jetzt wissen Sie, wie Sie zwischen Zimmern1
und2
. Erforschen Sie weiter durch die nächste unbekannte Tür, bis Sie den Ausgang finden!quelle
Da die Räume in den Ausgangslisten immer in der gleichen Reihenfolge sind, können wir eine schnelle Karte der Räume erstellen, während wir nach einem Ausgang suchen.
Mein Pseudocode ist etwas Javaisch, sorry, ich habe ihn in letzter Zeit oft benutzt.
Unvisited_Rooms ist eine Hashmap mit einer Raum-ID und einer Liste von Indizes der nicht zugeordneten Räume oder eines 2D-Arrays, je nachdem, was funktioniert.
Ich stelle mir vor, der Algorithmus könnte ungefähr so aussehen:
Sie müssen einen der allgemeinen Knotenpfadfinder für PathTo () - Räume auf der "Karte" verwenden. Hoffentlich ist das klar genug, um Ihnen den Einstieg zu erleichtern.
quelle
Ich bin mir Ihrer Anforderungen nicht ganz klar, aber wenn Folgendes zulässig ist, kann es ein einfach zu befolgender Algorithmus sein. Wahrscheinlich ein Fehler, zumal es eine rekursive Funktion verwendet. Außerdem wird geprüft, ob eine Tür zu dem Raum führt, aus dem Sie gekommen sind, aber ich weiß nicht, wie ein Rundweg mit drei Räumen funktionieren würde. In diesen drei Räumen wird möglicherweise eine ewige Schleife durchgeführt. Möglicherweise müssen Sie einen Verlauf hinzufügen, um sicherzustellen, dass kein Raum zweimal überprüft wird. Aber indem Sie Ihre Beschreibung lesen, ist dies möglicherweise nicht zulässig.
[Bearbeiten] 'ID' zur vorherigen Prüfung hinzugefügt und aktualisiert, um die Objektorientierung zu verbessern.
quelle
curRoom.doors <= 1
Daher kehrt die Funktion sofort zurück, da sie weiß, dass sie sich in einer Sackgasse befindet, aber denkt, dass sie bereits das gesamte Labyrinth erkundet hat.Die kurze Antwort lautet Tiefensuche mit Backtracking. Sie können zuerst die Breite machen, wenn Sie es vorziehen, aber Ihr kleiner Roboter wird dann viel mehr hin und her laufen.
Genauer gesagt, nehmen wir an, wir haben gegeben:
Um den Ausgang zu finden, brauchen wir nur:
Rufen Sie
escape()
mit der ID des Startraums an und der Roboter wird zum Ausgang gebracht (durch AnrufenmoveTo()
).quelle
escape(int startingRoom)
sollte anrufenescape(Stack<int> path)
if (path.contains(door)) continue;
verstößt gegen seine Anforderungen - der Agent weiß nicht wirklich, ob eine Tür zu einem Raum zurückführt, in dem er bereits war, es sei denn, er geht durch die Tür.