Puede usar un set
(en el sentido matemático de la palabra, es decir, una colección que no puede contener duplicados) para almacenar estados que ya ha visto. Las operaciones que necesitará para poder realizar esto son:
- insertando elementos
- probar si los elementos ya están ahí
Casi todos los lenguajes de programación ya deberían tener soporte para una estructura de datos que pueda realizar ambas operaciones en tiempo constante ( ). Por ejemplo:O(1)
set
en Python
HashSet
en Java
A primera vista, puede parecer que agregar todos los estados que ha visto a un conjunto como este será costoso en cuanto a memoria, pero no es tan malo en comparación con la memoria que ya necesita para su frontera; si su factor de ramificación es , su frontera crecerá en b - 1 elementos por nodo que visite (elimine 1 nodo de la frontera para "visitarlo", agregue b nuevos sucesores / hijos), mientras que su conjunto solo crecerá en 1 extra nodo por nodo visitado.sib - 11si1
En el pseudocódigo, un conjunto de este tipo (para nombrarlo closed_set
, para que sea coherente con el pseudocódigo en la wikipedia se podría usar en Breadth-First Search de la siguiente manera:
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
(algunas variaciones de este pseudocódigo también podrían funcionar, y serían más o menos eficientes dependiendo de la situación; por ejemplo, también podría tomar closed_set
para contener todos los nodos de los que ya ha agregado hijos a la frontera, y luego evitar por completo la generate_children()
llamada si current
ya está en el closed_set
.)
Lo que describí anteriormente sería la forma estándar de manejar este problema. Intuitivamente, sospecho que una "solución" diferente podría ser aleatorizar siempre el orden de una nueva lista de estados sucesores antes de agregarlos a la frontera. De esta manera, no evita el problema de agregar ocasionalmente estados que ya ha expandido anteriormente a la frontera, pero creo que debería reducir significativamente el riesgo de quedarse atascado en ciclos infinitos.
Tenga cuidado : no conozco ningún análisis formal de esta solución que demuestre que siempre evita ciclos infinitos. Si trato de "ejecutar" esto en mi cabeza, intuitivamente, sospecho que debería funcionar, y no requiere memoria adicional. Sin embargo, puede haber casos extremos en los que no estoy pensando en este momento, por lo que simplemente podría no funcionar, la solución estándar descrita anteriormente será una apuesta más segura (a costa de más memoria).