Iteracyjne pogłębianie wyszukiwania w głąb

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:

Graph.traversal.example.svg

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:

Dwukierunkowe IDDFS

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