Háló- (gráf-) algoritmusok Háló: G=(V,E), ahol V (vertex): csomópontok/csúcsok halmaza, E (edge): élek/ívek halmaza Élek egyediek, azaz InnenOda csak egyetlen él vezethet Ritka háló: ha |E| sokkal kisebb, mint |V|2 Hálók ábrázolási módjai: 1. Csúcsmátrixszal (sűrű hálókra) 2. Szomszédsági listával (ritka hálókra) 3. Éllistával (nem lehetnek egyedülálló pontok) Irányított/irányítatlan hálók (honnanhová hováhonnan) Súlyozott hálók: w:ER súlyfüggvény
További fogalmak Csomópont elődje: ahonnan él visz a csomópontba Csomópont utódja: ahová a csomópontból él visz Élek: csomópontok feletti relációk. Metarelációk: Szimmetria: innenoda onnanide (irányítatlan) Tranzitivitás: innenoda, onnanamodainnenamoda Reflexivitás: innenide Nyelő: csomópont, aminek nincsen utódja Forrás: csomópont, aminek nincsen elődje Fa: olyan gráf, csomópontjainak legfeljebb egyetlen elődje, és legfeljebb egyetlen forrása (gyökere van), és több nyelője (levele) is lehet. Körmentes háló: innenide eljutni sem közvetlenül, sem közvetve nem lehetséges
További fogalmak Út a hálóban a-tól z-ig: ab,bc,…,yz élsorozat Egyedülálló (szingli-szinguláris) csomópont, ha legfeljebb sajátmagával van összekötve Egybefüggő a háló, ha minden két csomópontja között létezik összekötő út (figyelem!! Rossz definíció!!) Széteső (disjoint) a háló, ha vannak csomópontok, amelyek között nincs összekötő út Ritka a háló, ha |E|<<|V|2 Egyébként sűrű.
Példa 1-2 1-4 2-3 2-5 3-6 4-2 5-4 6-6 1 2 3 4 5 6 1 . * . * . . 2 . . * . * . 3 . . . . . * 4 . * . . . . 5 . . . * . . 6 . . . . . * 1-2.4 2-3,5 3-6 4-2 5-4 6-6 1 2 4 5 3 6 Csúcsmátrix: inkább sűrű hálókra, de egyszerűbbek az algoritmusok - átlója az önmagából önmagába futó élek - irányítatlan gráf az átlóra szimmetrikus Szomszédsági listák: helytakarékos az ábrázolás, különösen ritka hálókra, viszont bonyolultabbak az algoritmusok Éllisták: csak egyedülálló pontok nélküli hálókra
Alapvető hálóalgoritmusok Adott csomópont elődeinek-utódainak-szomszédainak meghatározása Gráf transzponáltja: minden él irányításának megfordítása (csúcsmátrix: tükrözés az átlóra) Gráf komplementere: minden él az ellentettjére változik (csúcsmátrix: invertálás) Szimmetrikus lezárás: egyesítés a transzponálttal Tranzitív lezárás: minden innenoda, onnanamoda út esetében az innenamoda él felvétele
Bejárási/keresési algoritmusok széltében keresés (breadth first) Egy adott kezdőpontból kiindulva bejárja a többi csúcsot A kezdőcsúcsból vagy az adott rétegből egy lépésben elérhető csúcsok alkotják az első/a következő réteget Rátalál minden elérhető csúcsra (eléri a sajtot) Kiszámítja a legrövidebb (a legkevesebb élből álló) utat Létrehoz egy ún. szélességi fát: amelynek a gyökere a kezdőcsúcs, ágai pedig a keresés lépései A szélességi fát is rétegenként hozza létre
Az algoritmus működése Csúcspontok gyűjtése: réteg, új réteg, már bejárt Kezdetben egyetlen csúcs sem bejárt Az első csúcsot bejárjuk, beillesztjük a rétegbe Ciklus kezdete Belső ciklus: Felépítjük az új réteget: a régi rétegből elérhető, de még nem bejárt elemeket betesszük az új rétegbe, és betesszük a már bejártak közé Rétegváltás: az aktuális réteg az új réteg Ciklus vége: kilépünk, ha a réteg üres
Pár szó az algoritmusról Ha mindegyik csúcsra ráírjuk az elérési réteg(hullám) sorszámát, akkor megkapjuk a csúcsok távolságát a kezdőponttól Egyszerűsítés: ha csak egy „réteg” változónk van. amely egy FIFO sor. Innen vesszük le az elemeket az elérhetőségi vizsgálathoz, és ennek a végére illesztjük az elérhetőket. Keresés esetén: az elem megtalálásakor az algoritmus megáll
Példa az algoritmus működésére v w x y r s t u v w x y r s t u v w x y r s t u v w x y r s t u v w x y r s t u v w x y
Az algoritmus pszeudokódja SzéltébenBejár(s) for minden uÎV-{s} csúcsra do bejárt[u]=False, táv[u]=végtelen, előd[u]=NIL bejárt[s]=True, táv[s]=0 előd[s]=NIL, réteg={s} while réteg<>NIL u=réteg.feje for minden vÎutód(u) do if not bejárt[v] then bejárt[v]=True, táv[v]=táv[u]+1 előd[v]=u, réteg.beszúr(v) réteg.kivesz Kezdőértékezés minden csúcsra A kiindulási csúcs kezdőértékezése Az előd feljegyzése állítja elő a szélességi fát
Széltében/mélységben keresés a köv. hálón: tombKezd = Array(1, 1, 2, 3, 3, 3, 4, 4, 5) tombVeg = Array(2, 3, 4, 4, 6, 5, 7, 5, 6) Call myGraph.bfSearch(1) Call myGraph.dfSearch(1) 1 2 3 4 5 7 6 7 2 4 5 1 3 6
Elemzés Futási idő: Függ az adatok ábrázolásától. (Réteg, Újréteg, Már bejártak) Minden csomópontot legfeljebb egyszer vizsgálunk meg Tehát a sorba betétel és kivétel ideje: O(V). Minden ívet legfeljebb csak egyszer vizsgálunk meg (a szomszédok vizsgálatakor) ezek időszükséglete O(E). Teljes időszükséglet: O(E+V)
Széltében keresés - Legrövidebb utak s-ből v-be vezető legrövidebb úthosszat jelöljük δ(s,v)-vel Lemma: ha létezik uv él, akkor δ(s,v)<=δ(s,u)+1 Biz: Ha u elérhető, akkor legrosszabb esetben az uv plusz lépéssel elérhető v is. Ha u nem elérhető, akkor δ(s,u)=végtelen Lemma: A SzéltébenKeresés algoritmus táv[u] értékei minden vÎV csúcsra kielégíti a táv[v]>=δ(s,v) egyenlőtlenséget. Biz: Teljes indukcióval a réteg-be beállítás szerint 1. Kiindulás: amikor s-t beállítjuk a „réteg” sorba, akkor táv[s]=0, tehát táv[s]<=0<= δ(s,s) teljesül. 2. Indukciós lépés: Tfh. Egy u csúcsra táv[u]>=δ(s,u) teljesül, és egy v csúcs az u utódjainak vizsgálata során még nem bejárt. Ekkor (program értékadás, előzőek miatt): táv[v]=táv[u]+1>= δ(s,u)+1>= δ(s,v) táv[v]>= δ(s,v)
Széltében keresés - Legrövidebb utak Lemma: Tfh. „réteg” a (v1,v2,…,vr) csúcsokat tartalmazza. Ekkor táv[vr]<=táv[v1]+1 és táv[vi]<=táv[vi+1] bármely 1<=i<=r-1 értékre Biz: Sor műveletek szerinti teljes indukcióval 1. Kiindulás: a „réteg” sor csak az s csúcsot tartalmazza, az állítás triviálisan teljesül 2. Indukciós lépés: egy elem kivétele: Ha a sor üres lesz, akkor triviálisan teljesül. Ha nem lesz üres, akkor is teljesül. Egy elem betétele: az új, vr+1csúcs az éppen vizsgált v1 utódja. Ezért (algoritmus): táv[vr+1]=táv[v1]+1 Viszont: táv[vr]<=táv[v1]+1 (indukciós előfeltevés miatt), vagyis táv[vr]<=táv[vr+1].
Széltében keresés - Legrövidebb utak Tétel: táv[v]= δ(s,v) minden s-ből elérhető vÎV csúcsra. Biz: A távolság szerinti indukcióval, csak az elérhető csúcsokra. Jelölje Vk az s-től k távolságra levő csúcsokat. Vk={vÎV: δ(s,v)=k} Alapeset: V0={s}, táv[s]=0= δ(s,v), ez a legrövidebb út Indukciós lépés: Tfh., hogy adott a Vk halmaz, vagyis amelyre δ(s,u)=k. Ekkor u utódjainak vizsgálata során a köv. lehetőségek vannak: - v már bejárt. (vÎVi valamely i<=k-ra). Ezt ilyenkor nem vesszük hozzá a Vk+1 halmazhoz. - v még nem járt be. Ilyenkor a δ(s,v)<=δ(s,u)+1 miatt δ(s,v) legfeljebb 1-gyel nő meg, ha v még nem bejárt, akkor éppen 1-gyel.
Mélységi bejárás/keresés Balfal tapogatás+Ariadné fonala Szekvenciális (1 dalia) algoritmus Minden elágazásnál a legbaloldalsóbbat választjuk ki először, azon haladunk tovább Ha már tovább nem tudunk haladni, akkor visszalépünk, és az utolsó elágazási pontnál a következő alternatívát választjuk Kiindulás: vagy egy indulási ponttól vagy ciklusban az el nem ért pontokból A Prolog programozási nyelv alapmechanizmusa mélységi keresésen alapul
Példa az algoritmus működésére v w x r s t v w x Az első kiindulási pont Új kiindulási pont r s t v w x r s t v w x r s t v w x r s t v w x Az első kiindulási pontból elérhető elemeket elértük r s t v w x
Az algoritmus pszeudokódja MélységbenMindentBejár for minden uÎV forrás csúcsra do MélységbenBejár(u) MélységbenBejár(u) bejárt[u]=True, belépés[u]=most, most=most+1 for minden vÎutód(u) do if not bejárt[v] then előd[v]=u, MélységbenBejár(v) kilépés(u) = most, most=most+1
Megjegyzések Az algoritmus előállít egy mélységi erdőt. Ezt azonban nem rétegenként, hanem a legbaloldalibb ágon azonnal leás, majd vissszalép az utolsó nyitott csomópontig Időelemzés: 1. A MélységbenBejár eljárás egyszer hívódik meg minden csomópontra (Θ(|V|)) 2. Az utód(U) függvény egyszer hívódik meg minden élre (Θ(|E|)) Teljes időszükséglet: (Θ(|V|+|E|))
További állítások Zárójelezés tétele: mélységi keresés során bármely u,v csúcspárra igaz, az alábbiak közül pontosan egy: - belép-kilép[u], ill. belép-kilép[v] diszjunktak, u-v nem leszármazottjai egymásnak - belép-kilép[u] tartalmazza belép-kilép[v]-t, és u a v leszármazottja - u-v-re nézve fordítva Biz: A mélységi fában/erdőben két csomópont - vagy egyik a másik leszármazottja - vagy nem (oldalági, vagy független) A mélységi keresés a fában a leszármazottak feldolgozását 1-gyel később kezdi, és 1-gyel hamarabb fejezi be, mint az adott elemét bármely leszármazott feldolgozása részintervalluma az adott elemének.
Példa visszamutató él y z s t x w v u előre mutató él s t s t z v u z kereszt él (s (z (y (x x) y) (w w) z) s) (t (v v) (u u) t)
Éltípusok a mélységi erdőben Fa él: a mélységi erdő élei: u-v a fa éle, ha v-t először az u-ból kiindulva 1 lépésben értük el Visszamutató él: u-v visszamutat, ha v megelőzője u-nak a mélységi fában Előremutató él: u-v előre mutat, ha v leszármazottja u-nak a mélységi fában Kereszt él: az összes többi él Színezés/élosztályozás: - fehér: még be nem járt él - szürke: a mélységi fa gyökeréből vezető út - fekete: többi már bejárt csúcs
A mélységi bejárás további tulajdonságai A MélységbenBejár algoritmus élosztályozást célzó módosítása: - ha az utód még nem bejártfa él - ha az utód rajta van a gyökérből idevezető útonvisszamutató él - egyébként: előre mutató vagy kereszt él Irányítatlan háló mélységi keresésekor a vizsgált él: - vagy fa él - vagy visszamutató él Biz: tfh. az (u,v) él esetén v leszármazottja u-nak. Ekkor: - ha levél felé haladáskor vesszük figyelembefa él - ha a v élt más úton már elértük, akkor visszamutató él
Topologikus rendezés Topologikus rendezés irányított körmentes hálókra: a csúcsok sorbarendezése úgy, hogy ha létezik {x,y} él, akkor x előzze meg y-t. (Vagyis minden él egyirányba mutasson). Használata: 1. pl. projektterv, csomópontjai: részfeladatok, élei:előfeltétel kapcsolatok. Topologikus rendezéslehetséges forgatókönyv (több topologikus rendezés is elképzelhető) 2. Pl. egy szoftver modul eljárásainak rendezése, ha rekurzió nem megengedett…
Példa:öltözködési terv Topologikus rendezési algoritmus változat (párhuzamos-szerű) a hálóról lépésenként leválasztjuk a forrás csomópontokat ha ilyen nincs, akkor nincs topologikus rendezés a háló nem körmentes Házi feladat: körmentesség vizsgálata RDBMS környezetben. (Segítség: 1. Jelképezzék FROM,TO INTEGER mezők a hálót 2. DELETE SQL utasítással ciklusban töröljük a forrás csomópontokat zokni alsónadrág cipő nadrág ing öv nyakkendő zakó alsónadrág nadrág zokni cipő ing öv nyakkendő zakó
Mélységi keresésen alapuló topologikus rendezési algoritmus TopologikusRendezés: MélységbenMindentBejár hívása úgy, hogy a csomópontokat a feldolgozásukból kilépés időpontja szerint csökkenő listába gyűjtjük zokni (17/18) karóra (9/10) alsónadrág (11/16) Egyedülálló csomópont (2. fa gyökere) nadrág (12/15) cipő (13/14) ing (1/8) Harmadik fa gyökere nyakkendő (2/5) öv (6/7) Ennél a gyökérpontnál indul zakó (3/4) zokni alsónadrág nadrág cipő karóra ing öv nyakkendő zakó
Topologikus rendezés helyessége Tétel: a háló körmentes nincs a mélységi keresés során visszamutató él Biz: tfh. Létezik (u,v) visszamutató él. Viszont v őse u-nak a mélységi fában. Vagyis uvu kört képezellentmondás : tfh. a háló mégis tartalmaz kört. Legyen v a c kör elsőnek elért eleme, és (u,v) a c kör v-t megelőző eleme. v-ből kiindulva elérjük u-t, ekkor azonban v már bejárt lesz, vagyis (u,v) vissza mutat
Topologikus rendezés helyessége Tétel: a mélységi keresésen alapuló algoritmus tényleg egy irányított körmentes háló topologikus rendezését állítja elő. Biz: Azt kell belátni, hogy u és v csúcsokra, ha v elérhető u-ból, akkor Kilépés[v]<Kilépés[u]. Amikor a MélységiKeresés során egy (u,v) élt megvizsgálunk, akkor v nem eshet a gyökérből az u-ba vivő útra, hiszen a gráf körmentes. Kétféle lehetőség állhat fenn: 1. v-t már más ágon bejártuk: ekkor nyilvánvalóan Kilépés[v]<Kilépés[u], 2. v új csúcs. Ekkor csak v feldolgozása után fejezzük be u feldolgozását, vagy Kilépés[v]<Kilépés[u]
Erősen összefüggő komponensek Def: G=(V,E) háló erősen összefüggő komponense a csúcsok egy olyan maximális U részhalmaza, hogy u,vÎV esetén uv és vu is fennáll. Jelentősége: nagyméretű hálókat komponensekre lehet bontani, így az eredeti feladat részfeladatokra osztható. cd abe a b c d h fg e f g h
Algoritmus ErősenÖsszefüggő 1. MélységbenMindentBejár hívása, Kilépés[u] kilépési időpontok kiszámításával 2. Transzponált kiszámítása 3. MélységbenMindentBejár hívása a transzponáltra, de a főciklusban a csúcsokat Kilépés[u] szerint csökkenően járjuk végig 4. Az így kapott mélységi erdő fái adják az erősen összefüggő komponenseket
Segédtételek Lemma: Ha u és v ugyanazon erősen összefüggő komponens része, akkor a közöttük vezető utak nem vezetnek ki a komponensből. Biz: tfh. w egy komponensen kívüli csúcs, az uv út része (uwv). w csak akkor lehet a komponensen kívül, ha u nem elérhető w-ből, ill. w nem elérhető v-ből. Viszont wvu és vuw mert egy komponensben vannak, és uw. Mivel az erősen összefüggő komponens maximális méretű, ezért w benne van. v w u
Segédtételek Tétel: Egy gráf mélységi erdejében egy v csúcs leszármazottja az u csúcsnak ha u elérésekor (a Belép(u) időpillanatban) v a gráfban elérhető az u-ból olyan úton, amelynek még egyetlen szakasza sem volt bejárva Biz: tfh. v u leszármazottja. Ilyenkor Belép(u)<Belép(v), vagyis v-t az u elérésekor még nem járhattuk be. Indirekt: tfh. v elérhető u-ból a Belép(u) pillanatban még nem bejárt csúcsokon át, v mégsem lesz leszármazottja a mélységi fában. Tfh. v az első ilyen nem leszármazott csúcs. Legyen w a v csúcs közvetlen elődje a gráfban (esetleg w=u). Ekkor Belép(u)<= Belép(w)< Kilép(w)<=Kilép(u). Viszont Belép(u)<Belép(v), és Kilép(v)<=Kilép(w). Vagyis u be-kilépési intervalluma teljesen tartalmazza v-ét. Ezért (korábbi tétel) v u leszármazottja kell, hogy legyen. v w u Még be nem járt csúcsok
Segédtételek Tétel: Mélységi keresés esetén egy erősen összefüggő komponens csúcsai ugyanabba a mélységi fába kerülnek Biz: Legyen r az EÖK első olyan csúcsa, amelyet a mélységi fa elért. Ilyenkor - az EÖK összes csúcsába vezet az r-ből út - egyetlen ilyen út sem hagyja el az EÖK-t (a komponensből kilépve nem kanyarodik vissza) - az EÖK összes csúcsa r leszármazottja lesz a mélységi fában r
Segédtételek - Ősapa Be fogjuk látni, hogy minden EÖK-ben van egy csúcs, amelyet először érünk el, és utoljára hagyunk elősapa… Def: egy EÖK-beli csúcs ősapjának azt a φ(u) csúcsot nevezzük, amely u-ból elérhető, és utoljára hagyjuk el a mélységi keresésben. Vagyis u φ(u) és Kilép(φ(u)) maximális. φ(u)=u előfordulhat, mert uu. Ezért Kilép(u)<=Kilép(φ(u)) Belátjuk, hogy φ(φ(u))=φ(u), vagyis az ősapa (egy EÖK-ben) egyértelmű. Bármely u,vÎV csúcsra uv Kilép(φ(v))<=Kilép(φ(u)), hiszen {w:uw}Í{w:vw}, és φ a legkésőbbi kilépési időpontú csomópontot adja. Mivel uφ(u), ezért Kilép(φ(φ(u)))<=Kilép(φ(u)). Viszont Kilép(φ(u))<=Kilép(φ(φ(u))) Kilép(φ(φ(u)))=Kilép(φ(u))
Segédtételek - Ősapa Az ősapa az EÖK reprezentáns csúcsa, ezt érjük el először, és ezt hagyjuk el először. A transzponált keresése közben ez lesz a mélységi fa gyökere Tétel: G=(V,E) irányított gráfban bármely uÎV csúcs φ(u) ősapja megelőzője lesz u-nak G bármelyik mélységi keresése során Biz: Ha épp az ősapáról van szó (φ(u)=u), akkor a tétel nyilvánvalóan igaz. Ha (φ(u)<>u), akkor vizsgáljuk meg a csúcsokat a Belép(u) időpontban. Ha φ(u) már bejárt és el is hagyott, akkor Kilép(φ(u))<Kilép(u), ami ellentmond. Ha φ(u) megelőzője u-nak a mélységi fában, akkor a tétel igaz. Azt kell még belátnunk, hogy φ(u) nem lehet még nem bejárt: - Ha az u φ(u) út minden csúcspontja még nem bejárt lenne, akkor φ(u) az u leszármazottja kell hogy legyen. Ekkor viszont Kilép(φ(u))<Kilép(u), ami ellentmond a korábbi egyenlőtlenségnek. - Ha van már bejárt közbülső csúcs, akkor legyen t az u φ(u) út utolsó ilyen csúcsa. Ekkor t-nek a φ(u) megelőzőjének kell lennie a mélységi fában, mert a t-t követő összes csúcs még nem bejárt. Ha tehát φ(u) t leszármazottja, akkor Kilép(t)>Kilép(φ(u)), ami ellentmond az ősapa definíciónak.
Segédtételek - Ősapa Tétel: A G=(V,E) irányított gráfban az u,vÎV csúcsok ugyanahhoz az EÖK-höz tartoznakha G mélységi keresésekor ugyanaz az ősapjuk. Biz: Ha u és v ugyanahhoz az EÖK-höz tartoznak, akkor minden u-ból elérhető csúcs elérhető v-ből is, tehát φ(u)= φ(v). Tfh. φ(u)=φ(v) ekkor u és φ(u) ugyanabba az EÖK-be tartozik, de ugyanez teljesül v-re és φ(v)-re is. Vagyis u és v is ugyanahhoz az EÖK-höz tartozik.
Következmények Egy EÖK egy olyan csúcshalmaz, amelyeknek ugyanaz az ősapjuk. Az ősapa a mélységi keresés során az elsőnek elért és az utolsónak elhagyott csúcs. A transzponált keresése során a legelső r csúcs (fordított elhagyási sorrend!) biztosan ősapja valamelyik EÖK-nak. Melyik ez az EÖK? Amelyeknek r ősapja, vagyis ahonnan r elérhető, vagyis amelyek r-ből elérhetők a transzponáltban. vagyis leválasztottuk az első EÖK-t. Ezután vesszük a következő legkésőbb elhagyott (és nem bejárt) r’ csúcsot, és újra végrehajtjuk az algoritmust.