Sie können a set
(im mathematischen Sinne des Wortes, dh eine Sammlung, die keine Duplikate enthalten kann) verwenden, um bereits gesehene Zustände zu speichern. Die Operationen, die Sie ausführen müssen, sind:
- Elemente einfügen
- Testen, ob bereits Elemente vorhanden sind
Nahezu jede Programmiersprache sollte bereits eine Datenstruktur unterstützen, die beide Operationen in konstanter ( ) Zeit ausführen kann . Zum Beispiel:O ( 1 )
set
in Python
HashSet
in Java
Auf den ersten Blick mag es so aussehen, als wäre das Hinzufügen aller Zustände, die Sie jemals zu einem solchen Set gesehen haben, in Bezug auf den Speicher teuer, aber es ist nicht schlecht im Vergleich zu dem Speicher, den Sie bereits für Ihre Grenze benötigen. Wenn Ihr Verzweigungsfaktor , wächst Ihre Grenze um b - 1 Elemente pro Knoten, den Sie besuchen (entfernen Sie 1 Knoten von der Grenze, um sie zu "besuchen", fügen Sie b neue Nachfolger / Kinder hinzu), während Ihr Satz nur um 1 zusätzliche wächst Knoten pro besuchtem Knoten.bb - 11b1
Im Pseudocode könnte eine solche Menge (nennen wir sie closed_set
, um mit dem Pseudocode auf Wikipedia übereinzustimmen, in einer Breitensuche wie folgt verwendet werden:
frontier = First-In-First-Out Queue
frontier.add(initial_state)
closed_set = set()
while frontier not empty:
current = frontier.remove_next()
if current == goal_state:
return something
for each child in current.generate_children()
if child not in closed_set: // This operation should be supported in O(1) time regardless of closed_set's current size
frontier.add(child)
closed_set.add(current) // this should also run in O(1) time
(Einige Variationen dieses Pseudocodes funktionieren möglicherweise auch und sind je nach Situation mehr oder weniger effizient. Sie können beispielsweise auch closed_set
alle Knoten enthalten, von denen Sie bereits Kinder zur Grenze hinzugefügt haben, und den generate_children()
Anruf dann vollständig vermeiden wenn current
ist schon in der closed_set
.)
Was ich oben beschrieben habe, wäre die Standardmethode, um dieses Problem zu lösen. Intuitiv vermute ich, dass eine andere "Lösung" darin bestehen könnte, die Reihenfolge einer neuen Liste von Nachfolgestaaten immer zufällig zu ordnen, bevor sie zur Grenze hinzugefügt werden. Auf diese Weise vermeiden Sie nicht das Problem, gelegentlich Zustände hinzuzufügen, die Sie bereits zuvor an die Grenze erweitert haben, aber ich denke, dies sollte das Risiko, in unendlichen Zyklen stecken zu bleiben, erheblich verringern.
Seien Sie vorsichtig : Ich kenne keine formale Analyse dieser Lösung, die beweist, dass sie immer unendliche Zyklen vermeidet. Wenn ich versuche, dies intuitiv durch meinen Kopf zu "laufen", vermute ich, dass es irgendwie funktionieren sollte und keinen zusätzlichen Speicher benötigt. Es kann Randfälle geben, an die ich momentan nicht denke, daher funktioniert es möglicherweise auch nicht. Die oben beschriebene Standardlösung ist sicherer (auf Kosten von mehr Speicher).