Iteracyjne pogłębianie wyszukiwania w głąb
Klasa | Algorytm wyszukiwania |
---|---|
Struktura danych | Drzewo , wykres |
Wydajność w najgorszym przypadku | , gdzie rozgałęzienia, a głębokość najpłytszego rozwiązania jest |
Złożoność przestrzeni w najgorszym przypadku |
W informatyce , iteracyjne pogłębianie przeszukiwania lub dokładniej iteracyjne pogłębianie przeszukiwania w głąb (IDS lub IDDFS) to strategia przeszukiwania przestrzeni stanów /grafów, w której wersja przeszukiwania w głąb o ograniczonej głębokości jest uruchamiana wielokrotnie z rosnącymi limitami głębokości, aż do cel zostaje znaleziony. IDDFS jest optymalny, podobnie jak przeszukiwanie wszerz , ale zużywa znacznie mniej pamięci; w każdej iteracji odwiedza węzły w drzewie wyszukiwania w tej samej kolejności, co przeszukiwanie w głąb, ale skumulowana kolejność, w której węzły są odwiedzane po raz pierwszy, jest faktycznie wszerz.
Algorytm dla grafów skierowanych
Poniższy pseudokod przedstawia IDDFS zaimplementowany jako rekurencyjny DFS o ograniczonej głębokości (zwany DLS) dla grafów skierowanych . Ta implementacja IDDFS nie uwzględnia już odwiedzonych węzłów i dlatego nie działa dla grafów nieskierowanych .
0 funkcja IDDFS(korzeń) jest dla głębokości od do ∞ do znalezienia, pozostała ← DLS(korzeń, głębokość) jeśli znaleziono ≠ null to zwróć znalezione inaczej jeśli nie pozostała to zwróć wartość null funkcja DLS(węzeł, głębokość) jest jeśli głębokość = 0 to jeśli węzeł jest celem, a następnie zwróć (węzeł, prawda ) w przeciwnym razie return ( null , true ) (Nie znaleziono, ale może mieć dzieci) w przeciwnym razie, jeśli głębokość > 0 , to any_remaining ← false foreach child węzła do znalezionego, pozostałe ← DLS(dziecko, głębokość-1) jeśli znaleziono ≠ null, a następnie wróć (znaleziono, true ) , jeśli pozostaje to any_remaining ← prawda (Co najmniej jeden węzeł znaleziony na głębokości, niech IDDFS się pogłębi) return ( null , any_remaining)
Jeśli węzeł docelowy zostanie znaleziony, DLS odwija rekurencję powracającą bez dalszych iteracji. W przeciwnym razie, jeśli na tym poziomie głębokości istnieje co najmniej jeden węzeł, pozostała flaga pozwoli IDDFS na kontynuację.
2-krotki są przydatne jako wartość zwracana, aby zasygnalizować IDDFS dalsze pogłębianie lub zatrzymanie, w przypadku gdy głębokość drzewa i członkostwo w celu są nieznane a priori . Inne rozwiązanie mogłoby zamiast tego używać wartości wartowniczych do reprezentowania nie znalezionych lub pozostałych wyników poziomu .
Nieruchomości
IDDFS łączy efektywność przestrzenną przeszukiwania w głąb i kompletność przeszukiwania wszerz (gdy współczynnik rozgałęzienia jest skończony). Jeśli rozwiązanie istnieje, znajdzie ścieżkę rozwiązania z najmniejszą liczbą łuków.
Ponieważ wielokrotne iteracyjne wizyty pogłębiające mogą wydawać się marnotrawstwem, ale okazuje się, że nie jest to tak kosztowne, ponieważ w drzewie większość węzłów znajduje się na najniższym poziomie, więc nie ma większego znaczenia, jeśli wyższe poziomy są odwiedzane wielokrotnie czasy.
Główną zaletą IDDFS w przeszukiwaniu drzew gier jest to, że wcześniejsze wyszukiwania mają tendencję do ulepszania powszechnie używanych heurystyk, takich jak heurystyka zabójcy i przycinanie alfa-beta , dzięki czemu dokładniejsze oszacowanie wyniku różnych węzłów na końcowym przeszukiwaniu głębokości może wystąpić, a wyszukiwanie kończy się szybciej, ponieważ odbywa się w lepszej kolejności. Na przykład przycinanie alfa-beta jest najskuteczniejsze, jeśli najpierw wyszukuje najlepsze ruchy.
Drugą zaletą jest responsywność algorytmu. Ponieważ wczesne iteracje używają małych wartości dla wykonują się niezwykle szybko Pozwala to algorytmowi na niemal natychmiastowe dostarczanie wczesnych wskazań wyniku, po których następuje udoskonalenie w . Gdy jest używany w środowisku interaktywnym, takim jak szachy , funkcja ta umożliwia programowi grę w dowolnym momencie z aktualnie najlepszym ruchem znalezionym w dotychczas zakończonym wyszukiwaniu. Można to sformułować jako korekurencyjnie każdą głębokość wyszukiwania dając lepsze przybliżenie rozwiązania, chociaż praca wykonywana na każdym kroku jest rekurencyjna. Nie jest to możliwe w przypadku tradycyjnego przeszukiwania w głąb, które nie daje wyników pośrednich.
Analiza asymptotyczna
Złożoność czasowa
Złożoność czasowa IDDFS w (dobrze wyważonym) drzewie jest taka sama jak w przypadku wyszukiwania wszerz, tj. , gdzie to współczynnik rozgałęzienia, głębokość celu
Dowód
W iteracyjnym wyszukiwaniu pogłębiającym węzły na głębokości rozwijane raz, te na głębokości aż do korzenia drzewa wyszukiwania, które re - jest rozszerzony razy Tak więc całkowita liczba rozszerzeń w iteracyjnym przeszukiwaniu pogłębiającym wynosi
gdzie to liczba rozszerzeń na głębokości , to liczba rozszerzeń na głębokości i tak dalej. Faktoryzacja daje
Teraz niech . Następnie mamy
To jest mniej niż nieskończona seria
do którego się zbiega
- , za
To znaczy mamy
za
ponieważ lub jest stałą niezależną od ), jeśli (tj. jeśli współczynnik rozgałęzienia , biegnący czas pierwszego iteracyjnego poszukiwania pogłębiającego w głąb wynosi .
Przykład
Dla i liczba to
Podsumowując, iteracyjne wyszukiwanie pogłębiające od głębokości głębokości tylko o około węzłów niż pojedyncze wyszukiwanie wszerz lub w ograniczone wyszukiwanie do głębokości kiedy .
Im wyższy współczynnik rozgałęzienia, tym niższy narzut wielokrotnie rozwijanych stanów, ale nawet gdy współczynnik rozgałęzienia wynosi 2, iteracyjne przeszukiwanie pogłębiające zajmuje tylko około dwa razy więcej czasu niż pełne przeszukiwanie wszerz. Oznacza to, że złożoność czasowa iteracyjnego pogłębiania jest nadal .
Złożoność przestrzeni
Złożoność przestrzenna IDDFS gdzie .
Dowód
Ponieważ IDDFS w dowolnym momencie jest zaangażowany w przeszukiwanie w głąb, musi przechowywać tylko stos węzłów reprezentujący gałąź drzewa, którą rozwija. znajduje rozwiązanie o optymalnej długości, maksymalna głębokość tego stosu wynosi , to
Ogólnie rzecz biorąc, iteracyjne pogłębianie jest preferowaną metodą wyszukiwania, gdy istnieje duża przestrzeń poszukiwań, a głębokość rozwiązania nie jest znana.
Przykład
Dla poniższego wykresu:
przeszukiwanie w głąb rozpoczynające się od A, zakładając, że lewe krawędzie na pokazanym grafie są wybrane przed prawymi i zakładając, że wyszukiwanie zapamiętuje poprzednio odwiedzone węzły i nie będzie ich powtarzać (ponieważ jest to mały graf), odwiedzi węzły w następującej kolejności: A, B, D, F, E, C, G. Krawędzie przechodzące w tym przeszukiwaniu tworzą drzewo Trémaux , strukturę mającą ważne zastosowania w teorii grafów .
Wykonanie tego samego wyszukiwania bez zapamiętywania wcześniej odwiedzonych węzłów powoduje odwiedzanie węzłów w kolejności A, B, D, F, E, A, B, D, F, E itd. na zawsze, złapanych w A, B, D, F , cykl E i nigdy nie osiągając C lub G.
Iteracyjne pogłębianie zapobiega tej pętli i dotrze do następujących węzłów na następujących głębokościach, zakładając, że przebiega od lewej do prawej, jak powyżej:
- 0: A
- 1: A, B, C, E
(Pogłębianie iteracyjne widziało teraz C, podczas gdy konwencjonalne przeszukiwanie w głąb nie.)
- 2: A, B, D, F, C, G, E, F
(Nadal widzi C, ale pojawiło się później. Widzi również E inną ścieżką i dwukrotnie zapętla się z powrotem do F.)
- 3: A, B, D, F, E, C, G, E, F, B
W przypadku tego wykresu, gdy zostanie dodana większa głębokość, dwa cykle „ABFE” i „AEFB” po prostu wydłużą się, zanim algorytm się podda i spróbuje innej gałęzi.
Powiązane algorytmy
Podobna do iteracyjnego pogłębiania jest strategia wyszukiwania zwana iteracyjnym przeszukiwaniem wydłużającym, która działa z rosnącymi limitami kosztu ścieżki zamiast z limitami głębokości. Rozszerza węzły w kolejności rosnącego kosztu ścieżki; dlatego pierwszym celem, jaki napotyka, jest cel o najniższym koszcie ścieżki. Ale iteracyjne wydłużanie pociąga za sobą znaczny narzut, co czyni go mniej użytecznym niż iteracyjne pogłębianie.
Pogłębianie iteracyjne A* to wyszukiwanie typu najlepszy pierwszy, które wykonuje iteracyjne pogłębianie na podstawie wartości „ f ” podobnych do tych obliczanych w algorytmie A* .
Dwukierunkowe IDDFS
IDDFS ma dwukierunkowy odpowiednik, który naprzemiennie przeprowadza dwa wyszukiwania: jedno rozpoczynające się od węzła źródłowego i poruszające się wzdłuż skierowanych łuków, a drugie rozpoczynające się od węzła docelowego i przebiegające wzdłuż skierowanych łuków w przeciwnym kierunku (od węzła głównego łuku do węzeł ogonowy). Proces wyszukiwania najpierw sprawdza, czy węzeł źródłowy i docelowy są takie same, a jeśli tak, zwraca trywialną ścieżkę składającą się z pojedynczego węzła źródłowego/docelowego. W przeciwnym razie proces wyszukiwania do przodu rozszerza węzły potomne węzła źródłowego (ustaw węzły nadrzędne węzła docelowego (zestaw ) sprawdza się, czy . Jeśli tak, zostanie znaleziona najkrótsza ścieżka. W przeciwnym razie głębokość wyszukiwania jest zwiększana i wykonywane są te same obliczenia.
Jednym z ograniczeń algorytmu jest to, że najkrótsza ścieżka składająca się z nieparzystej liczby łuków nie zostanie wykryta. Załóżmy, że mamy najkrótszą ścieżkę przeskoki wzdłuż łuków, wyszukiwanie do przodu rozpocznie się od i wyszukiwanie będzie kontynuowane od do . Obrazowo granice poszukiwań będą się wzajemnie przenikać, a zamiast tego zostanie zwrócona nieoptymalna ścieżka składająca się z parzystej liczby łuków. Zilustrowano to na poniższych schematach:
Jeśli chodzi o złożoność przestrzeni, algorytm koloruje najgłębsze węzły w procesie wyszukiwania do przodu, aby wykryć istnienie środkowego węzła, w którym spotykają się dwa procesy wyszukiwania.
źródłowy i docelowy znajdują się w różnych silnie połączonych komponentach, łuku pozostawiając , się nie zakończy.
Złożoność czasu i przestrzeni
Czas działania dwukierunkowego IDDFS jest określony wzorem
a złożoność przestrzeni jest dana przez
gdzie węzłów w najkrótszej ścieżce . Ponieważ złożoność czasu działania iteracyjnego pogłębiania przeszukiwania w głąb wynosi }
Pseudo kod
Funkcja Build-Path(s, μ, B) to π ← Znajdź najkrótszą ścieżkę (s, μ) (Rekursywnie oblicz ścieżkę do węzła przekaźnikowego) usuń ostatni węzeł z π return π B (Dołącz stos przeszukiwania wstecz)
funkcja Depth-Limited-Search-Forward (u, Δ, F) jest taka, że jeśli Δ = 0 , to fa ← fa {u} (Zaznacz węzeł) return foreach child of u do Depth-Limited-Search-Forward(child, Δ − 1, F)
funkcja Depth-Limited-Search-Backward(u, Δ, B, F) jest poprzedzona u do B jeśli Δ = 0 to jeśli u w F to powrót u (Osiągnięto zaznaczony węzeł, użyj go jako węzła przekaźnikowego) usuń węzeł główny B zwróć null foreach parent of u do μ ← Depth-Limited-Search-Backward(parent, Δ − 1, B, F) if μ
null , a następnie wróć μ usuń węzeł główny B, zwróć null
funkcję
Znajdź najkrótszą ścieżkę (s, t) jest , jeśli s = t, a następnie wróć <s> F, B, Δ ← ∅, ∅, 0 na zawsze wykonaj wyszukiwanie do przodu z ograniczeniem głębokości (s, Δ, F) foreach δ = Δ, Δ + 1 do μ ← wyszukiwanie z ograniczeniem głębokości do tyłu (t, δ, B, F) jeśli μ null, a następnie wróć Build-Path(s, μ, B) (Znaleziono węzeł przekaźnikowy) F, Δ ← ∅, Δ + 1