Minimalno vpeto drevo v grafu
Kruskalov algoritem

Avtor

Avtor: Tatjana Hernaus (Tatjana.Hernaus@guest.arnes.si)

DIRI
Študijsko leto 2004/05
 

Viri:
Jernej Kozak, Podatkovne strukture in algoritmi, DMFA Slovenije, Ljubljana 1986
Zapiski in prosojnice s predavanj, Matija Lokar

Kratek opis problema

Naj bo G povezan neusmerjen graf z uteženimi povezavami. Radi bi našli tako vpeto drevo, da bo vsota cen povezav v tem vpetem drevesu minimalna. Eden izmed algoritmov za reševanje tega problema je Kruskalov algoritem (J. B. Kruskal, njegova domača stran). Rešitev gradimo po korakih, na vsakem koraku dodamo novo povezavo. Problema se lotimo "na požrešen način": vedno izberemo najcenejšo povezavo, ki je še na voljo. Pri tem pazimo le, da ne dobimo cikla.

Algoritem

Implementacija algoritma

Najprej sem sestavila razred Kopica.
Kopico sem predstavila s tabelo. Ker pa potrebujemo za vsak element tabele tri podatke (ceno povezave in njeni krajišči), je tabela pravzaprav matrika m*3, kjer je m število elementov.

Konstruktorji:
Poleg osnovnega Kopica(int m), ki naredi prazno tabelo dolžine m, je v razredu še konstruktor Kopica(int[][] matrika), ki elemente dane matrike n*n preloži v kopico. Velikost kopice je m = n*(n-1)/2, ker je matrika zgornjetrikotna brez diagonale. Dodala sem še podoben konstruktor public Kopica(int[][] matrika, int neskoncno), ki sproti izloči neskončne povezave, tako je kopica malo manjša in imamo pozneje manj dela.

Metode:

To so le metode, ki jih potrebujem za rešitev te naloge, nekatere standardne metode, npr. vstaviVkopico(pod), prazna() in podobne manjkajo. Metode so public, čeprav bi vsaj kakšna lahko bila private, denimo popraviKopico().

Rešitev naloge je programček Kruskal.java, ki dela takole.

V programu je nekaj globalnih spremenljivk:

Postopek:

Metode:

Primeri

Najprej sem testirala razred Kopica - pričakovala sem težave pri sestavljanju kopice in pri brisanju iz nje (in program je v celoti izpolnil moja pričakovanja :) ). Testni program TestZaKopico.java izpisuje celoten postopek prelaganja elementov za izbran primer. Namesto razreda kopica uporablja razred KopicaTest s testnimi izpisi (v ukazno vrstico, ne na grafično površino).

Program KruskalTest.java pa preverja delovanje samega algoritma: v ukazno vrstico izpiše dano (fiksno) tabelo povezav kot zgornjetrikotno matriko ter spremlja dodajanje povezav in seznam poddreves med postopkom. Tabelo povezav lahko vnesemo prek vnosnega okna ali pa prepustimo izbor računalniku.

Dodatni komentarji

Časovna in prostorska zahtevnost algoritma:

Prebrati moramo zgornji trikotnik matrike n*n brez diagonale - to je n*(n-1)/2 operacij
in jih preložiti v tabelo - še n*(n-1)/2 prelaganj. Če izločimo neskončne povezave, je prelaganj manj.
Preurejanje tabele v kopico nas stane m*log m, kjer je m število povezav, to je nekje med n in n*(n-1)/2.
Sledi sam postopek:
Najcenejšo povezavo - vrhKopice - najdemo v konstantnem času,
nato je treba popraviti kopico - log m in
preveriti dopustnost izbrane povezave - pregled tabele prinese n operacij.
V n korakih je to skupaj O(n*n).
Pri zelo polnih tabelah je torej najdražje zlaganje kopice: če je m blizu n2, je to O(n2log n), če je graf bolj "prazen" - povezav je le malo več kot vozlišč - pa nas kopica stane samo O(n log n). Vsekakor se izplača takoj na začetku izločiti "neskončne" povezave, pri velikih matrikah to lahko opazno poceni postopek.
Skupna časovna zahtevnost je torej najmanj O(n2) in največ O(n2 log n).

Za primerjavo: Primov algoritem, ki shaja brez kopice, ima vedno T(n) = O(n2). Za grafe z malo povezavami je boljši od Kruskalovega, pri polnih grafih pa ga ta prehiti (vir: J. Kozak).

Prostorska zahtevnost: Poleg matrike povezav potrebujemo še tabelo za kopico, ki je dolga m = n*(n-1)/2 ne glede na dejansko število povezav in tabelo poddreves dolžine n ter matriko velikosti m * 3 za rešitev. Vse skupaj je torej O(n2).

V analizi ni upoštevano risanje grafa, ki seveda najbolj podraži postopek - časovno in prostorsko.

Če bi se odločili za drugačno predstavitev poddreves (opisana je tule), bi potrebovali n matrik dolžine n, torej smo prostorsko še vedno na O(n2). Časovno zahtevnost bi malo skrajšali. Verjetno se pri zelo polnih matrikah to ne izplača, ker je T(n) več kot kvadratna. Če pa je povezav malo in je T(n) samo O(n log n), bi morda veljalo poskusiti.

Problem minimalnega vpetega drevesa ni rešljiv, če graf ni povezan. Program sam tega ne preverja. Če sami vnašamo matriko povezav, moramo poskrbeti za smiselnost podatkov. Če določanje cen prepustimo računalniku, se tu lahko zatakne. Da ne bi bilo preveč težav, je "neskončna" samo približno vsaka tretja povezava, zato redko naletimo na nerešljiv problem. Program nas v tem primeru ne opomni (!) razen izjemoma: če je povezav manj kot vozlišč, pove, da bo šlo narobe in zakaj.

Grafični prikaz se mi zdi dovolj nazoren, ni pa estetsko dovršen. Zdelo se mi je, da bi mi oblikovanje vzelo preveč časa. Zato sem se lepemu izgledu odpovedala. Saj je vendar bistvo Kruskalovega algoritma optimizacija cene, ali ne? Čas pa je denar ...


Na koncu sem se "oglasila" na strani konzorcija w3c, kjer sem izvedela, da mi manjka !doctype značka. Še nekaj, kar se moram naučiti ...