set
수학적 단어로 (즉, 중복을 포함 할 수없는 모음) 수학 식을 사용하여 이미 본 상태를 저장할 수 있습니다. 이 작업을 수행 할 수 있어야하는 작업은 다음과 같습니다.
거의 모든 프로그래밍 언어는이 두 작업을 모두 일정하게 수행 할 수있는 데이터 구조를 이미 지원해야합니다 (O ( 1 )) 시각. 예를 들면 다음과 같습니다.
언뜻보기에, 당신이 보았던 모든 상태를 세트에 추가하는 것처럼 보이는 것은 메모리면에서 비싸지 만 이미 프론티어에 필요한 메모리와 비교할 때 그리 나쁘지는 않습니다. 분기 요인이비, 당신의 국경은 b − 1 방문한 노드 당 요소 (제거) 1 국경에서 노드를 "방문"하는 노드 비 새로운 후계자 / 어린이), 반면 세트는 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
.)
위에서 설명한 것은이 문제를 처리하는 표준 방법입니다. 직관적으로, 나는 다른 "솔루션"이 새로운 후계자 상태의 목록을 프론티어에 추가하기 전에 항상 무작위로 만드는 것일 수 있다고 생각합니다. 이 방법을 사용하면 이전에 이미 확장 한 상태를 때때로 추가하는 문제를 피할 수는 없지만 무한 사이클에 빠질 위험을 크게 줄여야한다고 생각합니다.
주의 : 나는 항상 무한 사이클을 피한다는 것을 증명하는이 솔루션의 공식 분석을 모른다. 직관적으로 내 머리를 통해 이것을 "실행"하려고하면 일종의 작업이 필요하다고 생각되며 추가 메모리가 필요하지 않습니다. 지금 당장 생각하지 않는 엣지 사례가있을 수 있으므로 단순히 작동하지 않을 수도 있습니다. 위에서 설명한 표준 솔루션이 더 안전한 내기입니다 (더 많은 메모리 비용).