Vous pouvez utiliser un set
(au sens mathématique du mot, c'est-à-dire une collection qui ne peut pas contenir de doublons) pour stocker des états que vous avez déjà vus. Les opérations dont vous aurez besoin pour effectuer ces opérations sont les suivantes:
- insertion d'éléments
- tester si des éléments sont déjà là
Presque tous les langages de programmation devraient déjà avoir un support pour une structure de données qui peut effectuer ces deux opérations en temps constant ( ). Par exemple:O ( 1 )
set
en Python
HashSet
en Java
À première vue, cela peut sembler ajouter tous les états que vous voyez à un ensemble comme celui-ci sera coûteux en mémoire, mais ce n'est pas trop mauvais par rapport à la mémoire dont vous avez déjà besoin pour votre frontière; si votre facteur de branchement est , votre frontière augmentera de b - 1 éléments par nœud que vous visitez (supprimez 1 nœud de la frontière pour le "visiter", ajoutez b nouveaux successeurs / enfants), tandis que votre ensemble ne grandira que de 1bb - 11b1 supplémentaire nœud par nœud visité.
En pseudocode, un tel ensemble (nommons-le closed_set
, pour être cohérent avec le pseudocode sur wikipedia pourrait être utilisé dans une recherche en largeur comme suit:
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
(certaines variantes de ce pseudocode peuvent également fonctionner et être plus ou moins efficaces selon la situation; par exemple, vous pouvez également prendre le closed_set
pour contenir tous les nœuds dont vous avez déjà ajouté des enfants à la frontière, puis éviter complètement l' generate_children()
appel si current
est déjà dans leclosed_set
.)
Ce que j'ai décrit ci-dessus serait la manière standard de gérer ce problème. Intuitivement, je soupçonne qu'une autre "solution" pourrait être de toujours randomiser l'ordre d'une nouvelle liste d'États successeurs avant de les ajouter à la frontière. De cette façon, vous n'évitez pas le problème d'ajouter occasionnellement des états que vous avez déjà étendus à la frontière, mais je pense que cela devrait réduire considérablement le risque de rester coincé dans des cycles infinis.
Attention : je ne connais aucune analyse formelle de cette solution qui prouve cependant qu'elle évite toujours les cycles infinis. Si j'essaie de «faire fonctionner» cela dans ma tête, intuitivement, je pense que cela devrait fonctionner, et cela ne nécessite aucune mémoire supplémentaire. Il y a peut-être des cas marginaux auxquels je ne pense pas pour le moment, donc cela pourrait tout simplement ne pas fonctionner, la solution standard décrite ci-dessus sera un pari plus sûr (au prix de plus de mémoire).