Ugrás a tartalomhoz

Adatstruktúrák és algoritmusok

Házy Attila, Nagy Ferenc (2009)

6.5. Adott csúcsból induló legrövidebb utak

6.5. Adott csúcsból induló legrövidebb utak

Egy gépkocsivezető a lehető legrövidebb úton szeretne eljutni az egyik városból (A-ból) a másikba (B-be). Hogyan határozhatjuk meg ezt a legrövidebb utat, ha rendelkezünk az ország teljes autós térképével, amelyen minden, két szomszédos útkereszteződés közötti távolságot bejelöltek?

Egyik lehetséges megoldás az, ha módszeresen előállítjuk az összes, A-ból B-be vezető utat azok hosszával együtt, és kiválasztjuk a legrövidebbet. Könnyű azonban azt belátni, hogy még ha kört tartalmazó utakkal nem is foglalkozunk, akkor is több millió olyan lehetőség marad, amelyek többsége egyáltalán nem érdemel figyelmet. Például, egy C-n keresztül vezető A és B közötti út nyilvánvalóan szerencsétlen útvonalválasztás lenne, ha C túl hosszú kitérőt jelent.

A legrövidebb utak problémában adott egy élsúlyozott, irányított gráf, ahol a súlyfüggvény rendel az élekhez valós értékeket. A út súlya az utat alkotó súlyainak összege:

Definiáljuk az -ból -be vezető A legrövidebb út súlyát az alábbi módon:

Az csúcsból csúcsba vezető legrövidebb úton egy olyan utat értünk, amelyre teljesül.

Az A-ból B-be vezető utak példájában az autós térképet egy gráf segítségével modellezhetjük: a csúcsok jelentik a kereszteződéseket, az élek szimbolizálják a kereszteződések közötti útszakaszokat, az élek súlyai pedig az útszakaszok hosszaira utalnak. Célunk olyan legrövidebb útnak a megtalálása, amely A-ból adott indul ki, és B-be érkezik.

Az élsúlyok a távolságokétól eltérő metrikákat is kifejezhetnek. Gyakran használják idő, költség, büntetés, veszteség vagy más olyan mennyiség megjelenítésére, amely egy út mentén lineárisan halmozódik, és amelyet minimalizálni szeretnénk.

Fokozatos közelítés

Ennek a fejezetnek az algoritmusai a fokozatos közelítés módszerét alkalmazzák. Minden csúcsnál nyilvántartunk egy értéket, amely egy felső korlátot ad az kezdőcsúcsból a -be vezető legrövidebb út súlyára. A -t egy legrövidebb-út becslésnek nevezzük.

Kezdetben a legrövidebb-út becsléseket és a szülőkre mutató értékeket a következő futási idejű eljárás állítja be.

A kezdeti értékek beállítása után -re nil, és minden -re áll fenn.

Egy él segítségével történő közelítés technikáját alkalmazzák. Egy ellenőrzésből áll, amelyik összeveti a csúcshoz ez idáig legrövidebbnek talált utat az csúcson keresztül vezető úttal, és ha ez utóbbi rövidebb, akkor módosítja a és értékeket. A közelítő lépés csökkentheti a legrövidebb-út becslés értékét, és átállíthatja a mezőt az csúcsra. Az alábbi kód az él közelítő lépését írja le.

Ennek a fejezetnek mindegyik algoritmusa meghívja az Egy-forrás-kezdőérték eljárást, majd az élekkel egymás után végez közelítéseket. Sőt a közelítés az egyetlen olyan lépés, amely megváltoztatja a legrövidebb-út becsléseket és a szülő értékeket. A fejezet algoritmusai abban különböznek egymástól, hogy hányszor és milyen sorrendben végzik el az élekkel a Közelít műveletet. Dijkstra algoritmusa minden éllel pontosan egyszer közelít. A Bellman-Ford-algoritmus az egyes élekkel többször végez közelítést.

6.5.1. Bellman-Ford algoritmus

A Bellman-Ford-algoritmus az adott kezdőcsúcsból induló legrövidebb utak problémáját abban az általánosabb esetben oldja meg, amikor az élek között negatív súlyúakat is találhatunk. Adott egy súlyfüggvénnyel súlyozott irányított gráf, ahol a kezdőcsúcs . A Bellman-Ford-algoritmus egy logikai értéket ad vissza annak jelölésére, hogy van vagy nincs a kezdőcsúcsból elérhető negatív kör. Ha van ilyen kör, az algoritmus jelzi, hogy nem létezik megoldás. Ha nincs ilyen kör, akkor az algoritmus előállítja a legrövidebb utakat és azok súlyait.

A Bellman-Ford-algoritmus a fokozatos közelítés technikáját alkalmazza, bármelyik csúcsnál az kezdőcsúcsból odavezető legrövidebb út súlyára adott becslését ismételten csökkenti mindaddig, amíg az eléri annak tényleges értékét. Az algoritmus akkor és csak akkor tér vissza igaz értékkel, ha a gráf nem tartalmaz a kezdőcsúcsból elérhető negatív köröket.

A következő ábra a Bellman-Ford-algoritmus működését egy 5 csúcsból álló gráfon mutatja be. A és kezdeti beállítása után az algoritmus menetet végez a gráf éleivel. Minden menet a 2-4. sorok for ciklusának egy iterációja, amely a gráf minden élével egyszer végez közelítést. A (b)-(f) ábrák az algoritmus állapotait mutatják az élekkel végzett négy menet mindegyike után. menet után az 5-8. sorok negatív kört keresnek, és a megfelelő logikai értéket adják vissza.

A Bellman-Ford-algoritmus futási ideje , mivel az 1. sor előkészítése idejű, a 2-4. sorokban az élekkel végzett menet mindegyike ideig tart, és az 5-7. sorok for ciklusa idejű.

A kezdőcsúcs az csúcs. A értékeket beleírtuk a csúcsokba, és a vastagított élek jelzik a szülő értékeket: ha () él vastagított, akkor . (a) Az első menet előtti helyzet. (b)-(f) Az egymást követő menetek után kialakult helyzetek. Az (f) rész a végleges és értékeket mutatja. A Bellman-Ford-algoritmus ebben a példában igaz értékkel tér vissza.

6.27. Tétel. Bellman-Ford-algoritmus helyessége

Egy súlyozott, irányított gráfon futtassuk a Bellman-Ford eljárást, ahol a súlyfüggvény a , és a kezdőcsúcs az s. Ha nem tartalmaz -ből elérhető negatív köröket, akkor az algoritmus igaz értéket ad vissza, tovább minden csúcsra , és a szülő részgráf egy gyökerű legrövidebb-utak fa lesz. Ha -ben van egy -ből elérhető negatív kör, akkor az algoritmus hamis értékkel tér vissza.

6.5.2. Dijkstra algoritmusa

Dijkstra algoritmusa az adott kezdőcsúcsból induló legrövidebb utak problémáját egy élsúlyozott, irányított gráfban abban az esetben oldja meg, ha egyik élnek sem negatív a súlya. Ebben az alfejezetben ennek megfelelően feltesszük, hogy minden élre . A Dijkstra algoritmusának futási ideje, egy jó megvalósítás mellett, gyorsabb, mint a Bellman-Ford-algoritmusé.

A Dijkstra-algoritmus azoknak a csúcsoknak az halmazát tartja nyilván, amelyekhez már meghatározta az kezdőcsúcsból odavazető legrövidebb-út súlyát. Az algotimus minden lépésben a legkisebb legrövidebb-út becslésű csúcsot választja ki, beteszi az -t az -be, és minden -ból kivezető éllel egy-egy közelítést végez. Az alábbi megvalósításban egy minimum-elsőbbségi sort alkalmazunk a -beli csúcsok nyilvántartására, amelyeket azok táv értékeivel indexeltünk. Az algoritmus feltételezi, hogy a gráf egy szomszédsági listával van megadva.

A kezdőcsúcs a bal oldali csúcs. A legrövidebb-út becsléseket a csúcsok belsejében tüntettük fel, és vastagított élek jelzik a szülő csúcsokat: ha vastag, akkor . A sötétebb szürke színű csúcsok az S halmazban vannak, és a fehér színűek a prioritási sorban. (a) Ez a 4-8. sorok while ciklusának első iterációja előtti helyzet. A sötét színű csúcs a legkisebb értékű csúcs, és az 5. sor ezt választja ki csúcsként. (b)-(f) A while ciklus soron következő iterációi utáni helyzetek. Minden részben a sötétebb színnel jelölt csúcsot mint csúcsot választja ki a következő iteráció 5. sora. Az (f) rész a végleges táv és értékeket mutatja.

A Dijkstra-algoritmus az ábrán bemutatott módon végzi az élekkel a fokozatos közelítést. Az 1. sor a táv és a értékeinek szokásos kezdeti beállítását végzi, majd a 2. sor az halmazt teszi üressé. A 4-8. sorok while ciklusának minden iterációja előtt fennáll a invariáns állítás. A 3. sor a minimum-elsőbbségi sort készíti elő úgy, hogy az kezdetben minden -beli csúcsot tartalmazzon; mivel ekkor az még üres, az invariáns állítás a 3. sor után teljesül. A 4-8. sorok while ciklusában egy csúcsot veszünk ki az halmazból (legelőször ), és hozzáadjuk az halmazhoz, tehát az invariáns állítás továbbra is igaz marad. Az csúcs tehát a legkisebb legrövidebb-út becslésű csúcs a -ben. Ezután a 7-8. sorok közelítést végeznek az -ból kivezető élekkel, ezáltal módosítjuk a becslést és a szülőt, feltéve, hogy a -hez az -n keresztül most talált út rövidebb, mint az ott eddig nyilvántartott legrövidebb út. Figyelembe véve azt, hogy a 3. sor után már egyetlen csúcsot sem teszünk bele a -ba, valamint azt, hogy mindegyik csúcsot egyetlenegyszer veszünk ki a -ból és tesszük át az -be, a 4-8. sorok while ciklusa pontosan -szer hajtódik végre.

A Dijkstra-algoritmus mohó stratégiát alkalmaz, hiszen minden a ,,legkönnyebb”, a ,,legközelebbi” csúcsot választja ki a -ből, hogy azután betegye az halmazba. A mohó stratégiák általában nem mindig adnak optimális eredményt, de amint a következő tétel és annak következménye mutatja, a Dijkstra-algoritmus szükségszerűen a legrövidebb utakat állítja elő.

6.28. Tétel. Dijkstra-algoritmus helyessége

Ha Dijkstra algoritmusát egy nemnegatív súlyfüggvénnyel súlyozott, kezdőcsúcsú irányított gráfban futtatjuk, akkor annak a befejeződésekor minden csúcsra teljesül, hogy táv .

6.29. Következmény. Ha Dijkstra algoritmusát egy nemnegatív súlyfüggvénnyel súlyozott, irányított kezdőcsúcsú gráfban futtatjuk, akkor annak befejeződésekor a szülő részgráf egy gyökerű legrövidebb-utak fa lesz.