您可以使用set
(从单词的数学意义上讲,即不能包含重复项的集合)存储您已经看到的状态。您需要执行的操作是:
几乎每种编程语言都应该已经支持可以在恒定()时间内执行这两个操作的数据结构。例如:O (1 )
set
在Python中
HashSet
在Java中
乍一看,将您曾经看到的所有状态添加到这样的集合中似乎在内存方面是昂贵的,但是与边界所需的内存相比,这还不错。如果你的分支因子为,你前沿将增长b - 1名,每个节点所访问的元素(除去1个从前沿节点,以“游”,添加b新的继任者/子女),而您的集将仅增长1个额外每个被访问节点的节点数。bb − 11个b1个
在伪代码中,可以在“广度优先搜索”中使用这样的集合(将其命名为closed_set
,与Wikipedia上的伪代码保持一致):
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
。)
我上面描述的将是处理此问题的标准方法。凭直觉,我怀疑一个不同的“解决方案”可能是在将新的继承国列表添加到边境之前总是对其顺序进行随机化处理。这样,您就不会避免偶尔添加先前已经扩展到边界的状态的问题,但是我确实认为这应该大大降低陷入无限循环的风险。
请注意:我不知道对此解决方案有任何形式上的分析,但可以证明它始终避免了无限循环。如果我试图直觉地“运行”此程序,我怀疑它应该可以工作,并且不需要任何额外的内存。可能有些情况我暂时不会考虑,因此也可能根本不起作用,上述标准解决方案将是一个更安全的选择(以增加内存为代价)。