Levits algoritm är en algoritm på grafer som hittar det kortaste avståndet från en av grafens hörn till alla andra. Algoritmen fungerar även för grafer med negativ kantvikt . Algoritmen används flitigt inom programmering och teknik.
Alternativ 1. Med tanke på ett nätverk av vägar som förbinder städerna i Moskva-regionen. Vissa vägar är enkelriktade. Hitta de kortaste vägarna från staden Moskva till varje stad i regionen (om du bara kan röra dig längs vägarna).
Alternativ 2. Det finns ett antal flygningar mellan världens städer, kostnaden är känd för varje. Kostnaden för ett flyg från A till B kanske inte är lika med kostnaden för ett flyg från B till A. Hitta den lägsta kostnadsrutten (eventuellt med transfers) från Köpenhamn till Barnaul .
En viktad riktad [1] graf utan loopar ges . Hitta de kortaste vägarna från någon hörn av grafen till alla andra hörn i denna graf.
Nedan är en populär och effektiv implementering av den modifierade Levits algoritm på speciella grafer från sajten e-maxx.ru. Dess skillnad från den "kanoniska" versionen är att elementet läggs till i kön i början och inte i slutet. Detta gör det möjligt att uppnå en vinst på vissa grafer, men leder i värsta fall till exponentiell körtid (se avsnittet Komplexitet).
Det är värt att omedelbart nämna att koden nedan inte är fullt fungerande, eftersom strukturen på grafen inte är inställd (på något sätt). Resultatet (som standard) är en situation där det finns en graf som består av tio (standard) isolerade hörn, och den kortaste vägen söks från vertex med index 1 till vertex med index 3 (standard).
#include <iostream> // för parstrukturen: #inkludera <verktyg> #inkludera <vektor> #inkludera <deque> // för INT_MAX-värde: #inkludera <climits> // för den omvända funktionen: #include <algoritm> använder namnutrymme std ; typedef par < int , int > edge ; typedef vektor < vektor < kant > > graf ; const int INFINITY = INT_MAX ; // Standardvärden för algoritmoperationen (antal grafens hörn, index för sökvägens initiala och sista hörn) int defaultNumber = 10 , defaultStart = 1 , defaultFinish = 3 ; int main () { int numberOfVertices = defaultNumber , startVertex = defaultStart , finishVertex = defaultFinish ; graf g ( antalOfVertices ); // Här läser vi strukturen på grafen (från någonstans, till exempel från en fil). // Förresten, dimensionen och antalet hörn för sökningen måste troligen // hämtas från samma källa. vektor < int > d ( numberOfVertices , INFINITY ); d [ startVertex ] = 0 ; vektor < int > tillstånd ( numberOfVertices , 2 ); state [ startVertex ] = 1 ; deque < int > q ; q . push_back ( startVertex ); vektor < int > p ( antalOfVertices , -1 ); medan ( ! q . tom ()) { int vertex = q . front (); q . pop_front (); state [ vertex ] = 0 ; för ( size_t i = 0 ; i < g [ vertex ]. storlek (); ++ i ) { int till = g [ vertex ][ i ]. först , längd = g [ vertex ][ i ]. andra ; if ( d [ till ] > d [ vertex ] + längd ) { d [ till ] = d [ vertex ] + längd ; if ( ange [ till ] == 2 ) q . push_back ( till ); annat om ( ange [ till ] == 0 ) q . push_front ( till ); p [ till ] = vertex ; ange [ till ] = 1 ; } } } if ( p [ finishVertex ] == -1 ) { cout << "Ingen lösning" << endl ; } annan { vektor < int > sökväg ; för ( int vertex = finishVertex ; vertex != -1 ; vertex = p [ vertex ]) väg . push_back ( vertex ); reverse ( bana .börja (), väg .slut ( ) ); för ( size_t i = 0 ; i < sökväg . storlek (); ++ i ) cout << sökväg [ i ] + 1 << ' ' ; } // för att köra från en icke-kommandorad (så att du kan se resultatet) cin . få (); returnera 0 ; }Låt matrisen D[1..N] innehålla de aktuella kortaste väglängderna. Inledningsvis fylls matrisen D med "oändlighets"-värden, förutom D[s] = 0. I slutet av algoritmen kommer denna matris att innehålla de sista kortaste avstånden.
Låt arrayen P[1..N] innehålla de nuvarande förfäderna. Förutom D-matrisen ändras P-matrisen gradvis under algoritmens gång, och i slutet av den antar den de slutliga värdena.
Inledningsvis placeras alla hörn i mängden M2, förutom vertexen V0, som är placerad i mängden M1.
Vid varje steg i algoritmen tar vi en vertex från uppsättningen M1 (vi tar det översta elementet från kön). Låt V vara det valda hörnet. Vi översätter denna vertex till mängden M0. Sedan tittar vi igenom alla kanter som kommer ut från denna vertex. Låt T vara den andra änden av den aktuella kanten (det vill säga inte lika med V), och L vara längden på den aktuella kanten.
Naturligtvis, varje gång array D uppdateras, bör värdet i array P också uppdateras.
Om algoritmen är felaktigt implementerad, genom att använda en deque istället för köerna M1' och M1'' och lägga till hörn från M0 till början av deque, kommer algoritmen att köras exponentiellt i värsta fall [2] , detta rekommenderas inte. På riktiga grafer har algoritmen visat sig ganska väl: dess körtid [3] .
I jämförelse med Dijkstras metod förlorar Levits metod i det faktum att vissa hörn måste bearbetas upprepade gånger, men vinner i enklare algoritmer för att inkludera och exkludera hörn från M1-uppsättningen. Experiment visar att för grafer med ett "geometriskt" ursprung, det vill säga för grafer byggda på basis av transportnätverk och verkliga avstånd, visar sig Levits metod vara den snabbaste. Han vinner även när det gäller storleken på programmet.
Levitts metod har också den fördelen framför Dijkstras att den gäller negativa båglängder (”båglängd” är trots allt bara ett namn som ger oss användbara associationer till verkligheten). Om vi antar att värdena för l(u) inte nödvändigtvis är positiva, blir lösningen av problemet med den kortaste vägen mycket mer komplicerad.
Den första svårigheten är att en enkel regel i Dijkstras metod för att bestämma slutgiltigheten av det beräknade avståndet till en viss båge går förlorad. Denna svårighet, som vi kommer att se senare, förbigås, men med viss förlust av metodens effektivitet (vi måste kontrollera alla bågar som leder till den givna vertexen).
Den andra svårigheten är allvarligare: för negativa längder kan grafen innehålla konturer med en negativ summa av båglängder (låt oss kalla sådana konturer "negativa"). Att lägga till en negativ kontur till banan minskar värdet på objektivfunktionen, och ju fler rundor av den negativa konturen vi lägger till, desto "bättre". Det är helt enkelt omöjligt att bli av med den oändliga minskningen av minimum, men det finns två vägar ut ur en svår situation (naturligtvis beror valet av utväg inte på oss, utan på att det praktiska problemet löses).
Algoritmer för grafsökning | ||
---|---|---|
Oinformerade metoder | ||
Informerade metoder | ||
Genvägar | ||
Minsta spännträd | ||
Övrig |