あなたは使用することができset
ますが、すでに見てきたことを店の状態に(つまり、重複を含めることはできません、コレクション、言葉の数学的な意味で)。これで実行できるようにする必要がある操作は次のとおりです。
- 要素の挿入
- 要素がすでにそこにあるかどうかのテスト
ほとんどすべてのプログラミング言語は、これらの操作の両方を一定の()時間で実行できるデータ構造をすでにサポートしているはずです。例えば:O(1)
set
Pythonで
HashSet
Javaで
一見すると、このようなセットにこれまでに見たすべての状態を追加することはメモリの点で高価になるように思えるかもしれませんが、すでにフロンティアに必要なメモリと比較してそれほど悪くはありません。あなたの分岐因子が、あなたのフロンティアで成長する B - 1つのノードあたりの要素をあなたが(削除訪れることを 1つの追加し、「訪問」にフロンティアからそれをノード bのあなたのセットのみで成長する一方で、新しい後継/子供)を 1つの余分訪問したノードごとのノード。bb−11b1
疑似コードでは、このようなセット(名前をit 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
。)
上記で説明したのは、この問題を処理する標準的な方法です。直感的に、私は別の「解決策」は、後継者状態の新しいリストの順序を常にランダム化してから、それらをフロンティアに追加することだと思います。このようにして、すでに前に展開した状態を時々追加する問題を回避することはできませんが、無限サイクルでスタックするリスクを大幅に削減できると思います。
注意してください:私はこのソリューションの正式な分析については知りませんが、無限サイクルを常に回避することを証明しています。直感的にこれを「実行」しようとすると、それは一種の作業であるはずであり、追加のメモリは必要ありません。私が今考えていないエッジケースがあるかもしれません、しかしそれは単にうまくいかないかもしれません、上記の標準的な解決策はより安全な賭けです(より多くのメモリを犠牲にして)。