Iteratívan mélyülő mélységi keresés

A Wikipédiából, a szabad enciklopédiából

A számítástechnikában az iteratívan mélyülő keresés (IDA) vagy pontosabban az iteratívan mélyülő mélységi keresés[1] (IDDFS) egy állapottér/gráf keresési stratégia, amely a mélységi keresés egy mélységkorlátozott verziója, melyben ismételten fut végig a gráf elemein egyre nagyobb mélységben, amíg meg nem találja a keresett elemet. Az IDDFS optimális, akárcsak a szélességi keresés, de sokkal kevesebb memóriát használ; minden iterációban az adott mélységben azonos módon járja be a gráf csúcsait, mint a mélységi keresés, de kumulált sorrendben a csúcsokat valójában a szélességi keresés sorrendjében látogatja meg először.

Algoritmus irányított gráfokra[szerkesztés]

A következő pszeudokód az iteratívan mélyülő mélységi keresést (IDDFS) mutatja be egy irányított gráfon alkalmazott rekurzív mélységkorlátozott mélységi keresés (DLS) szempontjából. Az IDDFS ezen megvalósítása nem veszi figyelembe a már meglátogatott csúcsokat, ezért nem működik irányítatlan gráfokon.

function IDDFS(root) is 
    for depth from 0 to ∞ do 
        found, remaining ← DLS(root, depth)
        if found ≠ null then
            return found
        else if not remaining then
            return null

function DLS(node, depth) is
    if depth = 0 then
        if node is a goal then
            return(node, true)
        else
            return (nulltrue)  (Nem találtuk meg, de még lehet gyerekeleme)

    else if depth > 0 then
        any_remaining ← false
        foreach child of node do
            found, remaining ← DLS(child, depth−1)
            if found ≠ null then
                return (found, true)
            if remaining then
                any_remaining ← true  (Legalább egy csúcsot találtunk az adott mélységben, ezért hagyjuk az IDDFS-t tovább dolgozni)
        return (null, any_remaining)

Ha megtaláljuk a keresett csúcsot, akkor a DLS megállítja a rekurziót és visszaadja, hogy nem kell több iteráció. Különben, ha van akár egy elem is azon abban a mélységben, akkor a remaining változó engedi az IDDFS függvényt tovább futni.

A rendezett kettesek hasznosak, mint visszatérési értékek, mert ezzel vezérelhetjük az IDDFS-t, hogy folytassa a mélyítést, vagy álljon meg, abban az esetben, ha nem tudjuk, hogy a keresett elem milyen mélységben található. Másik megoldásként használhatunk egy megkülönböztetett értéket, ami jelezni tudja, hogy nem találtuk meg a keresett értéket, vagy visszaadja a hátralévő csúcsokat.

Tulajdonságok[szerkesztés]

Az IDDFS egyesíti a mélységi keresés tárhatékonyságát és a szélességi keresés teljességét (ha véges számú él indul csak csúcsokból). Ha létezik megoldás, akkor a legrövidebb utat adja vissza (legkevesebb éllel rendelkező utat).[2]

Mivel iteratív mélyítésnél a csúcsokat többször is érintjük, pazarlásnak tűnhet az ilyen módon történő bejárás, de mivel a legtöbb csúcs a fa legalsó szintjén található, ezért a fentebbi szinteken lévő csúcsok érintése nagyságrendileg nem számít túl sokat.

Az IDDFS fő előnye a játék fa alapú keresésekben, hogy a keresés eleje hajlamos javítani a gyakran használt heurisztikus algoritmusokat - pl. a gyilkos lépés heurisztikáját vagy az alfa-béta vágást - így az egyes ágakra egy pontosabb becslést tud adni és a keresés korábban befejeződik, mert összességében jobb sorrendben dolgozzuk fel a fát. Például az alfa-béta vágás akkor a leghatékonyabb csak, ha elsőre a legjobb lépést vizsgálja.

A második előnye, hogy az algoritmus gyorsan reagál, mert a kezdeti iterálásoknál kis (mélység) -t használ, és ezeket extrém gyorsan tudja kiszámolni. Ez lehetővé teszi az algoritmus számára, hogy a keresés már a futás korai stádiumában, szinte azonnal jó irányba induljon, és növekedésével ez csak egyre pontosabbá válik. Interaktív környezetben, például sakkjátszó programban történő alkalmazás esetén ez a képesség lehetővé teszi a program számára, hogy bármikor elvégezhessen egy lépést, az addig a pillanatig elvégzett keresés aktuálisan legjobbnak ítélt lépéssel. Ezt megfogalmazhatjuk úgy is, hogy a keresés minden mélységnél egyre pontosabb választ ad corekurzív (a rekurzió ellentéte) módon, habár minden egyes lépésén belül egyébként rekurzívan jár el. A hagyományos mélységi keresés erre nem képes, nem tud bármely pillanatban eredményt adni.

Aszimptotikus elemzés[szerkesztés]

Időbonyolultság[szerkesztés]

Az IDDFS időbonyolultsága egy (kiegyensúlyozott) fában megegyezik a szélességi keresésével, azaz , ahol az elágazási tényező és pedig a keresett érték mélysége.

Bizonyítás[szerkesztés]

Egy iteratívan mélyülő keresésben a csomópontok adott mélységben 1-szer ágazik el , pedig kétszer ágazik el, és így tovább egészen a keresési fa gyökeréig, ami -szer ágazik el. Így az elágazások száma összesen egy iteratív mélységi keresésben:

ahol az elágazások száma mélységen, az elágazások száma mélységen és így tovább. Abban az esetben, ha kiemelünk -t, akkor a következő képletet kapjuk:

Most nézzük meg, mi történik, ha Így most a képletünk a következőképpen alakul:

Ez kevesebb, mint a végtelen sorozat

amely képlet a következőhöz konvergál

, ha

Vagyis, ahova eljutottunk az a következő:

, ha

Mióta tudjuk, hogy vagy biztosan egy konstans, amely független -től (mélység), ha (azaz, ha az elágazási tényező nagyobb mint 1), az iteratív mélységi keresés futási ideje .

Példa[szerkesztés]

Amennyiben és a számunk az alábbi lesz:

Mindent összevetve, egy iteratív mélységi keresés az 1-es mélységből lefelé haladva egészen mélységig csak körülbelül -al több csúcs elágazást tesz (ennyivel több csúcsot vizsgál) mint egy sima szélességi keresésben vagy mint egy mélységében limitált keresésben mélységig, amennyiben .[3]

Minél nagyobb az elágazási tényező, annál kevesebbszer kell vizsgálni a "helytelen" utakat (vagyis ezeket gyorsan ki tudjuk zárni), de még akkor is, ha az elágazási tényező az 2, az iteratív mélységi keresés csupán kétszer olyan hosszú ideig tart, mint egy teljes szélességi keresés. Ez azt jelenti, hogy az időbonyolultsága az iteratív mélyítésnek továbbra is lesz.

Tárbonyolultság[szerkesztés]

Az IDDFS tárbonyolultsága ahol a keresett elem mélysége.

Bizonyítás[szerkesztés]

Mivel az IDDFS bármely pillanatban egy mélységi keresést végez el, csak egy csúcspont ágat kell tárolnia egyszerre, amely ágat éppen vizsgál. Mivel biztosan megtalálja a keresett elemet a fában, jelen esetben a maximum mélysége lesz, ennélfogva a maximum tárigénye lesz.

Általánosságban, az iteratív mélyítés az előnyben részesített keresési módszer, amennyiben nagy keresési tárhelyünk van és a keresett elem mélysége nem ismert.

Példa[szerkesztés]

Vegyük példának a következő gráfot:

egy mélységi keresésben, amely A csúccsal kezdődik, feltételezve, hogy a fent látható gráf bal oldali éleit előbb választjuk ki, mint a jobb oldaliakat, továbbá feltételezve azt, hogy a keresés képes megjegyezni a korábban már meglátogatott csúcsokat, és nem vizsgálja őket többször (mivel a képen egy igazán kis gráf látható), a következő sorrendben fogja vizsgálni a csúcsokat: A, B, D, F, E, C, G. Az élek, amelyik ezt a gráfot alkotják egy Trémaux fát formáznak, mely egy nagyon fontos struktúrája a gráfelméletnek.

Ugyanezt a keresést végrehajtva, anélkül, hogy képesek lennénk emlékezni a korábban meglátogatott csúcsokra, a következő csúcs-látogatási sorrendet kapjuk: A, B, D, F, E, A, B, D, F, E, stb... egy végtelen folyamat, az A, B, D, F, E ciklus soha nem éri el a C-t vagy a G-t, így visszaemlékezés nélkül ez az iteráció örökké tartana.

Az iteratív mélyítés megakadályozza ezt a végtelen hurkot és el fogja érni a következő csúcsokat a megadott mélységeken, feltételezve, hogy balról-jobbra haladunk, ahogy fentebb is tárgyaltuk:

  • 0: A
  • 1: A, B, C, E

(Az iteratív mélyítés most már látja a C csúcsot, ezzel ellentétben a hagyományos mélységi keresés nem látja soha a végtelen ciklus miatt.)

  • 2: A, B, D, F, C, G, E, F

(Ez a lépés-sorozat továbbra is látja a C csúcsot, habár később mint az előbbi, de látja. Valamint eléri még az E csúcsot is, egy különböző útvonalon, és vissza iterál F-hez kétszer is.)

  • 3: A, B, D, F, E, C, G, E, F, B

Fent ábrázolt gráfunk esetében minél több mélységet adunk hozzá, a két ciklusunk: "ABFE" és "AEFB" útvonalak egyszerűen tovább fognak tartani, mielőtt az alogritmusunk feladja a harcot és megpróbál egy másik ágon elindulni.

Kapcsolódó algoritmusok[szerkesztés]

Van egy, az iteratív mélyülő mélységi kereshez hasonló keresési stratégia, amelyet iteratív hosszabbító keresésnek hívunk, amely növekvő él-súlyozási limitekkel dolgozik mélységi limitek helyett. Ez a keresés a súlyok növekedő sorrendjében vizsgálja az ágakat; ebből kifolyólag az első célcsúcs amellyel találkozik, az a legolcsóbb útvonalon lesz megtalálható. Az iteratív hosszabbító keresés azonban jelentősen költségesebb, ezért kevésbé használható, mint az iteratív mélyítés.

Az iteratív mélyítés A* algoritmus algoritmus egy "legjobbat először" módszerrel valósít meg iteratív mélységi keresést 'f' értékeken alapulva - hasonlóan az A* algoritmusban használt 'f' értékekhez.

 Kétirányú IDDFS[szerkesztés]

Az IDDFS-nek van egy kétirányú párja, amely két keresés között váltogat: az egyik a gyökértől indul és halad az éleken át a célcsúcs fele, a másik pedig a célcsúcstól indulva halad az éleken az ellentétes irányba (vagyis a gyökér fele). A keresési folyamat először megnézi, hogy a gyökér és a célcsúcs megegyeznek-e egymással, és ha igen, akkor visszatér egy egyetlen csúcsból álló triviális útvonallal (a gyökér és egyben célcsúccsal). Ellenkező esetben az előre felé haladó keresés összeköti a gyökér elemet a hozzá köthető gyermek csúcsokkal ( felállás), a visszafelé haladó keresés pedig kibővíti a célcsúcsot a szülőcsúcsaival ( felállás), majd ellenőriz és megvizsgálja, hogy -nak és -nek van-e közös csúcsa. Amennyiben igen, megtaláltuk a legrövidebb utat. Máskülönben az útvonalak tovább terjeszkednek és az algoritmus folytatódik.

Az algoritmusnak az egyik korlátozása, hogy azok a legrövidebb utak, amelyek páratlan számú élből állnak, nem lesznek észlelve. Tegyük fel, hogy megtaláltuk a legrövidebb utat Amennyiben a mélységünk elér a második ugráshoz, az élek mentén, az előrehaladó keresés folytatni fogja -hez -tól. A visszafelé haladó keresés pedig -től -hoz. Vagyis a keresési határok keresztül mennek egymáson, és inkább egy olyan úton halad tovább, amely szuboptimális,(nem megfelelő, vártnál rosszabb eredményű) és páros számok találhatóak meg az ívei mentén. Ez az alábbi diagramon van ábrázolva:

Kétirányú IDDFS

Ami a tárbonyolultságot illeti, az algoritmus a legmélyebb csúcspontokat megjelölheti az előrefelé haladó keresési folyamatban annak érdekében, hogy felismerje, ha egy középső csúcs a két keresési procedúra között találkozna.

További nehézsége a kétirányú IDDFS alkalmazásának az, hogy ha a forrás és a célcsúcs nem összefüggő gráfot alkotnak, pl. , és nincsen olyan él, amely elhagyja -et és belelép -be, a keresésnek soha nem lesz vége.

 Idő és tárbonyolultságok[szerkesztés]

A kétirányú IDDFs futási ideje a következő képlettel adható meg:

és a tárbonyolultság pedig az alábbival fejezhető ki:

ahol a csúcsok száma a legrövidebb útvonalon. Mivel az iteratív mélységi keresés futási időbonyolultsága -al fejezhető ki, a gyorsítás nagyjából a következő:

Pszeudokód[szerkesztés]

function Build-Path(s, μ, B) is
    π ← Find-Shortest-Path(s, μ) (Recursively compute the path to the relay node)
    remove the last node from π
    return π  B (Append the backward search stack)
function Depth-Limited-Search-Forward(u, Δ, F) is
    if Δ = 0 then
        F ← F  {u} (Mark the node)
        return
    foreach child of u do
        Depth-Limited-Search-Forward(child, Δ − 1, F)
function Depth-Limited-Search-Backward(u, Δ, B, F) is
    prepend u to B
    if Δ = 0 then
        if u in F  then
            return u (Reached the marked node, use it as a relay node)
        remove the head node of B
        return null
    foreach parent of u do
        μ ← Depth-Limited-Search-Backward(parent, Δ − 1, B, F)
        if μ  null then
            return μ
    remove the head node of B
    return null
function Find-Shortest-Path(s, t) is
   if s = t then
       return <s>
   F, B, Δ ← ∅, ∅, 0
   forever do
       Depth-Limited-Search-Forward(s, Δ, F)
       foreach δ = Δ, Δ + 1 do
           μ ← Depth-Limited-Search-Backward(t, δ, B, F)
           if μ  null then
               return Build-Path(s, μ, B) (Found a relay node)
           B ← ∅
       F, Δ ← ∅, Δ + 1

Jegyzetek[szerkesztés]

  1. Korf (1985). „Depth-first Iterative-Deepening: An Optimal Admissible Tree Search”. Artificial Intelligence 27, 97–109. o. DOI:10.1016/0004-3702(85)90084-0.  
  2. David Poole: 3.5.3 Iterative Deepening‣ Chapter 3 Searching for Solutions ‣ Artificial Intelligence: Foundations of Computational Agents, 2nd Edition. artint.info. (Hozzáférés: 2018. november 29.)
  3. Artificial Intelligence: A Modern Approach (1994. április 25.) 

Fordítás[szerkesztés]

Ez a szócikk részben vagy egészben az Iterative deepening depth-first search című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.