Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 15 / 15 Svoriniai grafai ~60 min
Teorija

Dijkstra, ir kodėl ji teisinga

Dijkstros algoritmas remiasi vienu teiginiu.

Kodėl jis teisingas

Kai iš eilės imi pigiausią dar neapdorotą mazgą, jo atstumas jau yra galutinis.

Įrodymas telpa į sakinį. Tarkim, iki mazgo v yra pigesnis kelias, kurio dar neradome. Tas kelias turi kirsti ribą tarp apdorotų ir neapdorotų mazgų — o jo pirmasis neapdorotas mazgas u yra pigesnis už v. Vadinasi, u būtų buvęs paimtas anksčiau, ne v.

Tai neįmanoma, jei briaunos nėra neigiamos. Nes tada „toliau kelyje" reiškia „ne pigiau". Ketvirtame žingsnyje pamatysi, kas nutinka, kai šita sąlyga sulaužoma.

Palygink su keturiolikta pamoka: ten BFS sluoksniai buvo tas pats argumentas paprastesniu pavidalu. Dijkstra yra BFS, kurioje sluoksnius pakeitė kaina.

Pats algoritmas

Dijkstra(from, to):
    dist[*] ← ∞;  dist[from] ← 0
    frontier ← {from su 0}

    kol frontier netuščias:
        v, d ← PIGIAUSIAS frontier elementas        // ← visa esmė
        jei done[v]: praleisk                       // pasenęs įrašas
        done[v] ← true
        jei v = to: grąžink kelią ir d
        kiekvienai briaunai (v → w, kaina c):
            jei d + c < dist[w]:
                dist[w] ← d + c
                prev[w] ← v
                įdėk w į frontier su d + c

Palygink su keturioliktos pamokos BFS. Skiriasi trys dalykai: dist masyvas vietoj seen, sąlyga d + c < dist[w] vietoj !seen[w], ir frontier atiduoda pigiausią, o ne seniausią.

Trečiasis skirtumas yra vienintelis, kuriam reikia struktūros.

Du frontier variantai

Krūva (13 pamoka): Push ir Pop kainuoja O(log V).

Tiesinė peržiūra: jokios struktūros; kiekviename žingsnyje peržiūrimi visi V mazgai ir pasirenkamas mažiausias. O(V).

Abu teisingi ir grąžina tą patį kelią. Penktame žingsnyje pamatuosi, kiek kainuoja skirtumas.

Pasenę įrašai: kodėl krūva turi daugiau elementų nei mazgų

Radus pigesnį kelią iki w, w jau gali būti krūvoje su senesne, brangesne reikšme. Ką daryti?

Yra du atsakymai.

Sumažinti raktą (decrease-key): surasti w krūvoje ir pataisyti jo reikšmę. Bet krūva nežino, kur w yra — reikėtų atskiro indekso, atnaujinamo per kiekvieną sukeitimą. Tryliktoje pamokoje pamatavai, kad sukeitimų yra 6 922 vienam 10 000 elementų pastatymui; kiekvienas jų pabrangtų.

Tinginė šalinimas (lazy deletion): tiesiog įdėti w dar kartą. Krūvoje bus keli to paties mazgo įrašai; pirmasis ištrauktas yra pigiausias, o likusieji atpažįstami pagal done[v] ir praleidžiami.

Antrasis variantas yra tas, kurį rašo transit, ir tas, kurį rašysi tu. Kaina: krūva laiko šiek tiek daugiau, negu turi mazgų. Kiek — pamatuosi ketvirtame žingsnyje.

Kaina

V ištraukimų po O(log V), E įdėjimų po O(log V):

O((V + E) log V) su krūva. O(V² + E) su tiesine peržiūra.

Retame grafe — o tavo vidutinis laipsnis yra 1.49 — pirmasis yra beveik tiesinis, antrasis — kvadratinis.