Levitin algoritmi on graafien algoritmi , joka löytää lyhimmän etäisyyden graafin yhdestä kärjestä kaikkiin muihin. Algoritmi toimii myös graafisille negatiivisille reunapainoille . Algoritmia käytetään laajasti ohjelmoinnissa ja tekniikassa.
Vaihtoehto 1. Moskovan alueen kaupungit yhdistävä tieverkosto. Jotkut tiet ovat yksisuuntaisia. Etsi lyhyimmät polut Moskovan kaupungista jokaiseen alueen kaupunkiin (jos voit liikkua vain teitä pitkin).
Vaihtoehto 2. Maailman kaupunkien välillä on useita lentoja, joiden hinta on tiedossa. Lennon hinta paikasta A paikkaan B ei välttämättä ole sama kuin paikasta B paikkaan A kulkevan lennon hinta. Etsi minimikustannusreitti (mahdollisesti siirroilla ) Kööpenhaminasta Barnauliin .
Painotettu suunnattu [1] graafi ilman silmukoita on annettu . Etsi lyhyimmät polut jostakin graafin kärjestä kaikkiin muihin tämän graafin kärkikohtiin.
Alla on suosittu ja tehokas muunnetun Levitin algoritmin toteutus erityisillä kaavioilla sivustolta e-maxx.ru. Sen ero "kanoniseen" versioon on se, että elementti lisätään jonoon alussa, ei lopussa. Tämä mahdollistaa vahvistuksen saavuttamisen joissakin kaavioissa, mutta johtaa pahimmassa tapauksessa eksponentiaaliseen käyntiaikaan (katso Monimutkaisuus-osio).
On syytä mainita heti, että alla oleva koodi ei ole täysin toimiva, koska kaavion rakennetta ei ole asetettu (millään tavalla). Tuloksena (oletusarvoisesti) on tilanne, jossa on graafi , joka koostuu kymmenestä (oletus) eristetystä pisteestä ja etsitään lyhintä polkua indeksin 1 kärjestä indeksin 3 kärkeen (oletus).
#include <iostream> // parirakenteelle: #include <apuohjelma> #sisällytä <vektori> #sisällytä < deque > // INT_MAX-arvolle: #include <climits> // käänteiselle funktiolle: #include <algoritm> käyttäen nimiavaruutta std ; typedef pari < int , int > reuna ; typedef vektori < vektori < reuna > > graafi ; const int INFINITY = INT_MAX ; // Algoritmin toiminnan oletusarvot (graafin kärkipisteiden määrä, polun alku- ja loppupisteiden indeksit) int oletusNumber = 10 , oletusAloitus = 1 , oletusFinish = 3 ; int main () { int numberOfVertices = oletusNumber , startVertex = oletusAloitus , finishVertex = oletusLopetus ; graafi g ( vertikkojen lukumäärä ); // Täällä luetaan graafin rakenne (jostain esim. tiedostosta). // Muuten, haun ulottuvuus ja kärkien lukumäärät todennäköisimmin // täytyy saada samasta lähteestä. vektori < int > d ( vertikkojen lukumäärä , INFINITY ); d [ alkuVertex ] = 0 ; vektorin < int > tila ( numberOfVertices , 2 ); tila [ alkuVertex ] = 1 ; deque < int > q ; q . push_back ( aloitusVertex ); vektori < int > p ( Vertisien lukumäärä , -1 ); kun ( ! q . tyhjä ()) { int vertex = q . edessä (); q . pop_front (); tila [ vertex ] = 0 ; for ( koko_t i = 0 ; i < g [ vertex ]. size (); ++ i ) { int to = g [ vertex ][ i ]. ensin pituus = g [ vertex ] [ i ]. toinen ; jos ( d [ to ] > d [ kärki ] + pituus ) { d [ to ] = d [ vertex ] + pituus ; jos ( tila [ to ] == 2 ) q . push_back ( to ); muuten jos ( tila [ to ] == 0 ) q . push_front ( to ); p [ to ] = kärki ; tila [ to ] = 1 ; } } } if ( p [ viimeistelyVertex ] == -1 ) { cout << "Ei ratkaisua" << endl ; } muu { vektori < int > polku ; for ( int vertex = viimeistelyVertex ; vertex != -1 ; vertex = p [ vertex ]) polku . push_back ( vertex ); käänteinen ( polku .alku (), polku .loppu ( ) ); for ( koko_t i = 0 ; i < polku . koko (); ++ i ) cout << polku [ i ] + 1 << ' ' ; } // suorittaaksesi ei-komentoriviltä (jotta näet tuloksen) cin . saada (); paluu 0 ; }Olkoon taulukko D[1..N] nykyisen lyhimmän polun pituudet. Aluksi taulukko D täytetään "äärettömyyden" arvoilla, paitsi D[s] = 0. Algoritmin lopussa tämä matriisi sisältää lopulliset lyhyimmät etäisyydet.
Olkoon taulukon P[1..N] nykyiset esivanhemmat. Kuten D-taulukko, myös P-taulukko muuttuu vähitellen algoritmin aikana ja saa lopulliset arvot sen lopussa.
Aluksi kaikki kärjet sijoitetaan joukkoon M2, paitsi kärki V0, joka sijoitetaan joukkoon M1.
Algoritmin jokaisessa vaiheessa otamme kärjen joukosta M1 (otamme ylimmän elementin jonosta). Olkoon V valittu kärki. Käännämme tämän kärjen joukoksi M0. Sitten katsomme kaikkien tästä kärjestä tulevien reunojen läpi. Olkoon T nykyisen reunan toinen pää (eli ei yhtä suuri kuin V) ja L nykyisen reunan pituus.
Tietysti joka kerta kun taulukkoa D päivitetään, myös taulukon P arvo tulee päivittää.
Jos algoritmi on toteutettu väärin, käyttämällä deque-merkkiä jonojen M1' ja M1'' sijasta ja lisäämällä pisteitä M0:sta dequen alkuun, algoritmi toimii pahimmassa tapauksessa eksponentiaalisesti [2] , tätä ei suositella. Todellisilla kaavioilla algoritmi on osoittautunut varsin hyvin: sen ajoaika [3] .
Verrattuna Dijkstran menetelmään Levitin menetelmä häviää siinä, että joitain pisteitä täytyy käsitellä toistuvasti, mutta voittaa yksinkertaisemmissa algoritmeissa, joissa M1-joukkoa otetaan ja jätetään pois. Kokeet osoittavat, että "geometrisen" alkuperän eli liikenneverkkojen ja todellisten etäisyyksien perusteella rakennetuille kuvaajille Levitin menetelmä osoittautuu nopeimmaksi. Hän voittaa myös ohjelman koon suhteen.
Levittin menetelmällä on myös se etu Dijkstran menetelmään verrattuna, että se soveltuu negatiivisiin kaaren pituuksiin (kunhan "kaaren pituus" on vain nimi, joka antaa meille hyödyllisiä assosiaatioita todellisuuteen). Jos oletetaan, että l(u):n arvot eivät välttämättä ole positiivisia, lyhimmän polun ongelman ratkaisusta tulee paljon monimutkaisempi.
Ensimmäinen vaikeus on, että Dijkstran menetelmän yksinkertainen sääntö tiettyyn kaareen lasketun etäisyyden lopullisuuden määrittämiseksi menetetään. Tämä vaikeus, kuten tulemme myöhemmin näkemään, ohitetaan, vaikka menetelmän tehokkuus heikkenee (joudumme tarkistamaan kaikki annettuun kärkeen johtavat kaaret).
Toinen vaikeus on vakavampi: negatiivisilla pituuksilla kaavio voi sisältää ääriviivoja negatiivisella kaaren pituuksien summalla (kutsutaanko tällaisia ääriviivoja "negatiivisiksi"). Negatiivisen ääriviivan lisääminen polkuun pienentää tavoitefunktion arvoa, ja mitä enemmän negatiivisen ääriviivan kierroksia lisäämme, sitä "parempi". On yksinkertaisesti mahdotonta päästä eroon minimin loputtomasta laskusta, mutta vaikeasta tilanteesta on kaksi tietä (tietenkin ulospääsyn valinta ei riipu meistä, vaan ratkaistavasta käytännön ongelmasta).
Kaavion hakualgoritmit | ||
---|---|---|
Tietämättömät menetelmät | ||
Tietoon perustuvat menetelmät | ||
Pikanäppäimet | ||
Vähintään ulottuva puu | ||
Muut |