Вы можете использовать set
(в математическом смысле слова, то есть коллекции, которая не может содержать дубликаты) для хранения состояний, которые вы уже видели. Для этого вам необходимо выполнить следующие операции:
- вставка элементов
- тестирование, если элементы уже там
Практически каждый язык программирования уже должен иметь поддержку структуры данных, которая может выполнять обе эти операции за постоянное ( ) время. Например:O ( 1 )
set
в Python
HashSet
на Яве
На первый взгляд может показаться, что добавление всех состояний, которые вы когда-либо видите, к набору, будет дорогим с точки зрения памяти, но это не так уж плохо по сравнению с памятью, которая вам уже нужна для вашей границы; если ваш коэффициент ветвления равен , ваша граница будет расти на элемент на узел, который вы посещаете (удалите узел с границы, чтобы "посетить" его, добавьте новых наследников / потомков), тогда как ваш набор будет расти только на лишний узел на посещаемый узел.б - 1 1 б 1бб - 11б1
В псевдокоде такой набор (назовем его так closed_set
, чтобы он соответствовал псевдокоду в википедии), можно использовать в поиске в ширину следующим образом:
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
(некоторые варианты этого псевдокода тоже могут работать и быть более или менее эффективными в зависимости от ситуации; например, вы можете также взять closed_set
все узлы, для которых вы уже добавили потомков, и затем полностью избежать generate_children()
вызова если current
уже в closed_set
.)
То, что я описал выше, будет стандартным способом решения этой проблемы. Интуитивно, я подозреваю, что другое «решение» могло бы состоять в том, чтобы всегда рандомизировать порядок нового списка состояний-преемников перед добавлением их к границе. Таким образом, вы не избежите проблемы случайного добавления состояний, которые вы ранее расширили до границы, но я думаю, что это должно значительно снизить риск застрять в бесконечных циклах.
Будьте осторожны : я не знаю ни одного формального анализа этого решения, которое доказывает, что оно всегда избегает бесконечных циклов. Если я попытаюсь «прогнать» это через мою голову, интуитивно, я подозреваю, что это должно работать, и это не требует дополнительной памяти. Хотя могут быть крайние случаи, о которых я сейчас не думаю, так что это также может просто не сработать, стандартное решение, описанное выше, будет более безопасной ставкой (за счет большего объема памяти).