You are on page 1of 35

VILNIAUS UNIVERSITETAS

MATEMATIKOS IR INFORMATIKOS FAKULTETAS


INFORMATIKOS KATEDRA

Poravimo algoritmai dinaminiuose grafuose


(Matching Algorithms in Dynamic Graphs)
Bakalauro baigiamasis darbas

Atliko: 4 kurso 1 grupės studentas


Kipras Narbutas (parašas)

Darbo vadovas:
lekt. Irmantas Radavičius (parašas)

Recenzentas:
asist., dr. Julius Andrikonis (parašas)

Vilnius
2019
Santrauka

Darbe analizuojami du algoritmai, tai klasikinis Vengrų algoritmas, bei dinaminis Vengrų
algoritmas. Vis didėjantys duomenų kiekiai, su kuriais dažniausiai nuolatos vyksta pakeitimai,
priverčia pasirinkti geresnius sprendimo būdus nei kad pasikeitus duomenims problemą spręsti iš
naujo.
Šio darbo tikslas – apžvelgti dinaminį Vengrų algoritmą ir palyginti jį su statiniu. Gauti
rezultatai leidžia nuspręsti, kada geriau pasirinkti dinaminį, o kada statinį Vengrų algoritmą.
Darbo pirmoje dalyje supažindinama su reikalingomis sąvokomis bei sprendimo metodais, o
antroje dalyje pateikiama algoritmų implementacija. Svarbiausia vieta yra trečioje dalyje, kur
atliekami eksperimentai bei gaunami rezultatai, kurie leidžia teigti ar dinaminis Vengrų algoritmas
yra vertas implementacijos ir jei taip, tai kada.

2
Summary

The paper analyzes two algorithms – the classical Hungarian algorithm and it’s dynamic
counterpart, the dynamic Hungarian Algorithm. The ever increasing amount of data, which is
constantly being accessed and modified, makes us choose better ways than to solve the problem
from scratch each time.
The aim of this work is to look at the dynamic Hungarian algorithm and compare it to the
static one. The results that we get will let us decide when to choose each algorithm. At the start
of this paper the most important concepts and notations are introduced. Then we analyze the
algorithms and their implementations. In the most important part, the third section of this paper
we will perform experiments and get results that will let us decide if the dynamic Hungarian
algorithm is worth implementing and if yes, then when.

3
Turinys
ĮVADAS ................................................................................................................................. 5
1. PRISKYRIMO PROBLEMA ............................................................................................ 7
1.1. Priskyrimo problemos apibrėžimas ............................................................................. 7

1.1.1. Priskyrimo problemos užrašymas ir pavyzdys ..................................................... 7


1.2. Grafai ir priskyrimo problema ..................................................................................... 8

1.2.1. Grafų poravimas ................................................................................................... 8


1.2.2. Dvidalis grafas ...................................................................................................... 8
1.2.3. Priskyrimo problemos užrašymas grafų poravimo pavidalu ................................ 9
1.2.4. Papildomi apibrėžimai .......................................................................................... 9
1.3. Dinaminiai grafai ....................................................................................................... 10

2. ALGORITMAI ................................................................................................................ 11
2.1. Vengrų algoritmas ..................................................................................................... 11

2.1.1. Apibrėžimas ........................................................................................................ 11


2.1.2. Algoritmas .......................................................................................................... 12
2.2. Inrekementinis algoritmas ......................................................................................... 13

2.2.1. Apibrėžimas ........................................................................................................ 13


2.2.2. Algoritmas .......................................................................................................... 13
2.3. Dinaminis algoritmas................................................................................................. 14

2.3.1. Apibrėžimas ........................................................................................................ 14


2.3.2. Algoritmas .......................................................................................................... 14
2.3.3. Pavyzdys ................................................................................................................. 15

3. ALGORITMŲ PALYGINIMAS ..................................................................................... 19


3.1. Testavimo aplinka ..................................................................................................... 19

3.2. Duomenys ir grafo vaizdavimas kompiuteryje.......................................................... 19

3.3. Duomenų generavimas .............................................................................................. 20

3.4. Algoritmai bei testavimo aplinka .............................................................................. 21

3.5. Eksperimentai ............................................................................................................ 22

3.6. Algoritmas ................................................................................................................. 31

REZULTATAI IR IŠVADOS.............................................................................................. 33
ŠALTINIAI ........................................................................................................................ 355

4
ĮVADAS

Pradėjus vystytis technologijoms, daugelį darbo vietų pradėjo keisti mašinos arba
programinė įranga. Ji gali visiškai pakeisti žmogų arba jam pagelbėti, kas žymiai padidina
efektyvumą ir sumažina produkcijos kaštus. Tačiau norint, kad programinę įrangą būtų verta
įsigyti, didelis dėmesys yra kreipiamas į algoritmus, kurie atlieka ir įgyvendina norimą
funkcionalumą.
Turint programinę įrangą, kuri naudoja efektyvius algoritmus, galima sutaupyti daug
resursų. Resursai gali būti pinigų, žaliavų ar kitų nematerialių formų pavidalu. Didelė dalis darbo
vietų, kur žmogus turi atlikti kokį nors darbų paskirstymą, gali būti automatizuota. Tai ir yra
daroma šiais laikais, nes kuo didesnis darbuotojų ir darbų skaičius, tuo ilgiau užtrunka tą
paskirstymą padaryti paprastam darbuotojui. Daugelyje atvejų algoritmas ne tik paskirstytų
darbus, tačiau rastų optimaliausią paskirstymą, kad resursų, įgyvendinti tiems darbams, būtų
sunaudota mažiausiai.
Algoritmas yra instrukcijų rinkinys, skirtas kokios nors užduoties sprendimui. Turint
užduotį, kurią reikia įgyvendinti, dažniausiai galima pasirinkti ne iš vieno, bet daugelio algoritmų.
Tai yra svarbu dėl to, kad turint kelis algoritmus, galima juos palyginti ir pasirinkti optimalų
variantą sau.
Į klausimą, kaip palyginti skirtingus algoritmus, sprendžiančius tą pačią užduotį, atsako
sudėtingumo teorija. Svarbiausi palyginimo kriterijai: algoritmo veikimo laikas, sunaudotos
atminties apimtis, ar jis apskritai baigia darbą ir ar galima jį vykdyti lygiagrečiai naudojant kelis
kompiuterius [AB09].
Taip pat aktualus klausimas, ką daryti, jeigu turimas didelis duomenų kiekis, kuris visada
keičiasi. Paprasčiausias būdas būtų paleisti turimą algoritmą iš naujo, tačiau turint daug duomenų
ir daug pakeitimų, tai gali būti labai lėtas būdas gauti sprendimą. Vienas iš pasirinkimų yra naudoti
dinaminius algoritmus, kurie, turint kažkokius duomenis su jau gautu sprendimu, gali atitinkamai
pakeisti kintamuosius ir išspręsti tik reikiamą dalį užduoties su pasikeitusiais duomenimis.
Nors ir ne visada, tačiau dažniausiai, kai užduočiai spręsti yra pasirenkamas algoritmo
automatizavimas, dirbama su dideliu duomenų kiekiu arba yra reikalingas labai greitas to
paskirstymo laikas. Vienas iš paskirstymo pavyzdžių, kai reikalingas labai spartus algoritmas, yra
transporto sektorius. Staiga užsidaręs kelias gali daryti įtaką transportavimo kaštams.

5
Jeigu taip atsitinka ir duomenys pasikeičia, galima priskyrimo užduotį spręsti iš naujo,
tačiau turint didelį duomenų kiekį kiekvieną kartą spręsti iš naujo gali būti prastas pasirinkimas.
Panaudojus dinaminį algoritmą, kuris leistų pataisyti turimą sprendimą atsižvelgiant į
pasikeitusius duomenis, būtų galima sutrumpinti algoritmo veikimo laiką.
Darbo tikslas
Sukurti algoritmą, kuris pasinaudodamas duomenimis apie grafą galėtų parinkti optimalesni
sprendimą kada naudoti statinį, o kada dinaminį Vengrų algoritmą.
Uždaviniai
• Sukurti įrankį, kuris padėtų sugeneruoti duomenis bei palyginti algoritmus;
• Implementuoti statinį ir dinaminį Vengrų algoritmus;
• Atlikti eksperimentus;
• Remiantis eksperimentų duomenimis implementuoti algoritmą, kuris galėtų parinkti
reikiamą sprendimo būdą atsižvelgiant į grafą.
Šiame darbe implementuoti du algoritmai skirti spręsti priskyrimo problemą. Vienas
statinis, o kitas dinaminis, kuris bando pataisyti statinio algoritmo pirmutinį sprendimą. Darbe bus
palyginti šie algoritmai ir bus atlikti eksperimentai norint sužinoti, kokiomis sąlygomis yra geriau
naudoti dinaminį, o kada statinį. Taip pat bus implementuotas algoritmas, kuris panaudodamas
mūsų eksperimentų metu gautus rezultatus pats parinks geresnį sprendimo būdą atsižvelgdamas į
grafą. Darbas sudarytas iš 3 dalių. Pirmame skyriuje apžvelgta, kas yra priskyrimo problema, kaip
ji užrašoma bei kokios grafo struktūros naudojamos norint ją užrašyti ir išspręsti. Toliau darbe,
antrame skyriuje, aprašomi pasirinkti algoritmai bei jų implementacijos detalės. Trečiame skyriuje
aprašomi reikalingi eksperimentai duomenims, jų generavimo būdas, įrankis palyginti algoritmus
ir eksperimentų rezultatai.

6
1. PRISKYRIMO PROBLEMA
Šiame skyriuje yra aprašoma, kas yra priskyrimo problema, pateikiamas jos aprašymas bei
pavyzdys. Taip pat skyriuje supažindinama su pagrindiniais terminais ir sąvokomis, kurios
naudojamos algoritmuose.

1.1. Priskyrimo problemos apibrėžimas


Priskyrimo problema iškyla, kai prieinami resursai (pvz. žmonės, kompiuteriai) atlikdami
skirtingus darbus turi skirtingus efektyvumo laipsnius. Priskyrimo problema yra optimalaus
priskyrimo n žmonių į n darbų radimas, darant išvadą, kad kiekvienam žmogui yra priskirtas
skaitinis sudėtingumas kiekvienam darbui. Optimalus priskyrimas yra toks, kur žmonėms paskirtų
darbų suma yra maksimumas [Mun57]. Yra n! galimų priskyrimų (iš kurių tik keli optimalūs),
taigi tampa labai sudėtinga apeiti visus galimus priskyrimus, o kai n labai didelis, netgi
neįmanoma. Problema yra surasti pakankamai veiksmingą algoritmą optimalaus priskyrimo
radimui.
Matematinis užrašymas yra toks: Tegul rij yra našumo įvertinimas žmogaus Mi darbui Jj.
Matricos elementų aibės yra skaitomos kaip nepriklausomos, jei nėra daugiau nei vienos toje
pačioje linijoje (žodis „linija“ taikomas tiek matricos eilutėms, tiek stulpeliams). Norima rasti aibę
n nepriklausomų elementų iš matricos (rij) taip, kad šių elementų suma būtų maksimali. Tegul
r = maxi, j rij ir tegul xij = r – rij . Lygiavertė problema yra n nepriklausomų elementų radimas iš
matricos A = (xij) taip, kad šių elementų suma būtų mažiausia. Manome, kad matricos A elementai
yra sveikieji skaičiai [Mun57].

1.1.1. Priskyrimo problemos užrašymas ir pavyzdys


Paprastas priskyrimo problemos pavyzdys būtų: tarkime, kad turime tris darbuotojus: Tomą,
Joną ir Petrą. Vienas iš jų turi plauti grindis, kitas – langus, o trečias – indus, tačiau kiekvienas iš
jų prašo skirtingo piniginio atlygio už kiekvieną darbą. Šiuo atveju problema – kaip paskirstyti
darbus, kad bendra išleistų pinigų suma būtų mažiausia. Šį pavyzdį galime atvaizduoti matricos
pavidalu:
1 lentelė. Pavyzdinio uždavinio matrica
Grindų plovimas Langų plovimas Indų plovimas

Tomas 1€ 4€ 5€

Jonas 5€ 7€ 6€

Petras 5€ 8€ 8€

7
Kaip matoma 1 lentelėje, kiekvienas darbuotojas atlieka darbus už tam tikrą kainą. Iš viso
galimi 6 paskirstymai. Tačiau iš jų tik du – optimalūs. Pirmas, kuriame Tomas turėtų plauti grindis,
Jonas indus, o Petras langus. Šis paskirstymas lentelėje paryškintas. Antras paskirstymas, kuriame
Tomas turėtų plauti langus, Jonas indus, o Petras grindis. Abu darbų paskirstymai kainuoja 15
eurų.

1.2. Grafai ir priskyrimo problema


Priskyrimo problemos duomenis (matricą) yra patogu užrašyti grafo pavidalu. Darbuotojai
atitinka vieną grupę viršūnių, o darbai – kitą. Norint išspręsti priskyrimo uždavinį ir rasti optimalų
darbų paskirstymą, tai galima užrašyti grafų poravimo pavidalu.

1.2.1. Grafų poravimas


Turėdami grafą G = (V, E), suporavimu M grafe1 G vadiname nepriklausomų (angl. non-
adjecent) briaunų poaibį, iš kurių nei viena nėra ciklas [BM76]. Kitaip tariant, nėra dviejų briaunų,
kurios dalintųsi ta pačia viršūne. Viršūnė yra suporuota, jeigu ji yra vienos iš briaunų pabaigos
taškas. Kitu atveju viršūnė yra nesuporuota.
Priskyrimo problemos sprendimas yra sudarytas iš maksimalaus svorio poravimo (arba
minimalaus svorio tobulo poravimo) radimo svoriniame dvidaliame (angl. Bipartite) grafe
[BM76]. Maksimalus poravimas turi didžiausią briaunų skaičių, tačiau minimalaus svorio
maksimalus poravimas gali būti paverstas į minimalaus svorio tobulą poravimą. Pridėdami
dirbtines briaunas su labai dideliais svoriais, dvidalį grafą galime paversti į pilną dvidalį grafą. Šie
svoriai turėtų būti didesni nei bet kokie egzistuojantys grafe, kad apsaugotų nuo šių netikrų briaunų
atsiradimo optimaliame rezultate.

1.2.2. Dvidalis grafas


Grafų teorijoje, dvidalis – tai grafas, kurio viršūnės gali būti padalintos į dvi nesusikertančias
ir nepriklausomas aibes U ir V taip, kad kiekviena briauna sujungia viršūnę iš U aibės su viršūne
iš V aibės [BM76].
Kad palengvintume darbą, galime manyti, kad visi mūsų dvidaliai grafai, kuriuos naudosime,
bus pilni. Tai reiškia, kad dvi nepriklausomos viršūnių aibės U ir V bus vienodo dydžio.
Taip pat norėdami rasti priskyrimo problemos sprendimą, turime nagrinėti svorinį dvidalį
grafą. Tai reiškia, kad kiekviena briauna turi kokį nors svorį.

1
Grafas – tam tikrų viršūnių, sujungtų briaunomis ir/arba lankais, rinkinys. Grafu vadinsime pora G = (V, E),
kur V kokia nors aibė, o E – aibės V elementų porų aibė. Aibės V elementai vadinami grafo G viršūnėmis, o aibės E
elementai – briaunomis [BM76].
8
1.2.3. Priskyrimo problemos užrašymas grafų poravimo pavidalu
Priskyrimo problemos užrašymui naudojame grafo briaunų kainų matrica.

1 pav. Būdas pavaizduoti matricos briaunų svorius – kainų matrica

2 pav. Dvidalis grafas sudarytas pagal 1 pav. kainų matrica

Kaip matome, 2 pav. yra nepilnas dvidalis grafas. Jo briaunos sujungia darbuotojus (viršūnės
kairėje) su darbais (viršūnės dešinėje).

1.2.4. Papildomi apibrėžimai


Taigi, kiekviena viršūnė gali būti suporuota arba nesuporuota, dar kitaip vadinama atvira.
Briaunos taip pat gali būti suporuotos arba nesuporuotos. Jei viršūnė vi yra suporuota su viršūne
uj, uj vadinsime vi pora. 3 pav. galima pamatyti suporuotas ir nesuporuotas viršūnes. Suporuotos
viršūnės skiriamos pilnomis linijomis, o nesuporuotos taškinėmis.
Alternuojantis takas yra toks takas per grafą, kad po kiekvienos suporuotos briaunos eina
nesuporuota briauna ir atvirkščiai. 3 pav. būtų kaip alternuojančio tako pavyzdys (v3, u4, v2, u5).

9
Auginantis takas yra toks alternuojantis takas, kuris prasideda ir baigiasi nesuporuota
viršūne. 3 pav. toks pats takas kaip ir prieš tai buvęs alternuojantis.
Vengrų medis yra sudarytas iš visų alternuojančių takų, prasidedančių iš kurios nors
nesuporuotos viršūnės. Auginančio tako ieškojimas yra alternuojančių takų peržiūra grafo
paieškos platyn principu, o procesą galima pavadinti Vengrų medžio auginimu. Vengrų medį,
prasidedantį viršūnėje v3, galime pamatyti 4 pav.

3 pav. Dvidalis grafas su suporuotomis 4 pav. Vengrų medis prasidedantis nuo v3 viršūnės
ir nesuporuotomis viršūnėmis

1.3. Dinaminiai grafai


Daugelyje grafo algoritmo pritaikymo vietų, tokiose kaip komunikacijos tinklai ar transporto
infrastruktūra, grafai yra ta vieta, kurioje gali įvykti dažni, tam tikri pasikeitimai. Tokie
pasikeitimai apima naujų viršūnių pridėjimą ar atėmimą (inkrementinimas (angl. incrementing)
arba dekrementinimas (angl. decrementing)), taip pat briaunų svorių pokyčius.
Dinaminio grafo algoritmo tikslas yra efektyviai atnaujinti problemos sprendimą, po to, kai
grafe įvyko pakitimai, vietoj to, kad problema būtų sprendžiama iš naujo.
Dėl didelio kiekio problemos pasikeitimo galimybių yra daug sunkiau sukurti dinamines
struktūras ar algoritmus, ir jas išanalizuoti, nei statinių algoritmų [EGG98].

10
2. ALGORITMAI
Šiame poskyryje supažindinama su algoritmais priskyrimo problemai spręsti, kurie naudoja
grafų poravimo metodą. Šiame darbe implementuoti ir palyginti du algoritmai. Pirmiausia, tai
klasikinis Vengrų algoritmas su greitesne implementacija ir dinaminis Vengrų algoritmas, kuris
naudoja statinio algoritmo sprendinį, kad pasikeitus duomenims galėtų greičiau pataisyti sprendinį,
užuot iš naujo sprendęs visą uždavinį pasinaudojus statinį algoritmą.

2.1. Vengrų algoritmas


2.1.1. Apibrėžimas
Vengrų algoritmas yra klasikinis ir vienas iš pirmųjų efektyvių algoritmų skirtų priskyrimo
problemai spręsti. 1955 metais Vengrų metodą išrado ir publikavo Harold Kuhn ir pavadino jį
„Vengrų metodu“ dėl to, kad šis algoritmas didžiąja dalimi yra sujungtas kitų dviejų Vengrų
matematikų, Dénes Kőnig ir Jenő Egerváry, darbas [Kuh56].
James Munkres peržiūrėjęs algoritmą, 1957 metais nustatė, kad jis yra stipriai polinomiškas.
Nuo tada algoritmas žinomas dar kaip Kuhn-Munkres algoritmas [Mun57].
Originalaus Vengrų algoritmo sudėtingumas yra O(𝑛4 ) [Mun57], tačiau laikui bėgant, po to,
kai atsirado originalus Vengrų algoritmas, kiti mokslininkai, tokie kaip J. Edmonds ir Richard M.
Karp pastebėjo, kad jį galima pagerinti ir gauti greitesnį sprendimo būdą.
Algoritmo strategija yra priskirti dualius kintamuosius α𝑖 kiekvienai viršūnei v𝑖 ir dualius
kintamuosius β𝑖 kiekvienai viršūnei u𝑖 . Algoritmas panaudoja faktą, kad dualumas minimizacijos
priskyrimo problemoje yra galimas, jei α𝑖 + β𝑖 ≤ 𝑐𝑖𝑗 [CK98]. Vengrų algoritmas viso darbo metu
išlaiko galimas α𝑖 ir β𝑖 reikšmes. Briauna laikoma leistina (angl. admissible), jei α𝑖 + β𝑖 = 𝑐𝑖𝑗 .
Subgrafas, kurį sudaro tik leistinos briaunos, vadinamas lyginamuoju subgrafu.
Pradėjus nuo tuščio suporavimo, pagrindinė algoritmo strategija yra pastovus auginančių
takų ieškojimas lyginamajame subgrafe. Jei randamas toks takas, esama suporavimų aibė yra
auginama apkeičiant suporuotas ir nesuporuotas briaunas šiame take. Kadangi yra viena daugiau
nesuporuota, nei suporuota, briauna, apkeitimas padidina suporavimą vienetu, taip atlikdamas
vieną algoritmo stadiją. Jei auginantis takas nerandamas, dualūs kintamieji pakeičiami, kad būtų
galima pridėti papildomų briaunų į lyginamąjį subgrafą, padarydamas tas briaunas leidžiamomis
ir paieška tęsiama. Algoritmas reikalauja n stadijų praėjimų, per kuriuos randamas n suporavimų
skaičius.
Kiekviena algoritmo stadija užima O(𝑛2 ) laiko, jei jis implementuotas teisingai [CK98].
Kadangi yra n stadijų skaičius, algoritmo sudėtingumas yra O(𝑛3 ).

11
2.1.2. Algoritmas
Įvestis: Dvidalis grafas, {V, U, E} (kur |V| = |U| = n) ir 𝑛 × 𝑛 matricos svorio matrica
Išvestis: pilnas suporavimas M
1) Inicializacija:
a) Pradedame nuo tuščio suporavimo M0 = ∅
b) Priskiriame galimas reikšmes dualiems kintamiesiems αi ir βj pagal taisykles:
∀vi ∈ V, αi = 0
∀ui ∈ U, βj = min (cij)
2) Atliekame n pakartojimų algoritmo žingsnio Stadiją.
3) Grąžiname suporavimą po n-tosios stadijos: M = Mn
Stadija:
1. Paskiriame kiekvieną nesuporuotą viršūnę iš V kaip Vengrų medžio šaknį.
2. Auginame Vengrų medžius nesuporuotose viršūnėse lyginamajame subgrafe. Viršūnių vi
indeksus i, sutinkamus Vengrų medyje, paskiriame aibei I*, o indeksus j viršūnių uj aibei
J*. Jei randamas auginantis takas, einame į 4 žingsnį, jei ne, medžiai negali būti daugiau
auginami, einame į 3 žingsnį.
3. Keičiame dualiuosius kintamuosius α ir β kaip parodyta žemiau, kad galėtume pridėti
naujas briaunas į lyginamąjį subgrafą. Tada grįžtame į 2 žingsnį, kad toliau ieškotume
auginančio tako.
1
Θ= min (𝑐𝑖𝑗 − α𝑖 − βj )
2 𝑖∈𝐼 ∗ ,𝑗∉𝐽∗

α + Θ 𝑖 ∈ 𝐼∗
α𝑖 ← { 𝑖
α𝑖 − Θ 𝑖 ∈ 𝐼 ∗
α + Θ 𝑗 ∈ 𝐽∗
βj ← { 𝑖
α𝑖 − Θ 𝑗 ∈ 𝐽 ∗
4. Auginame esamą poravimą apkeisdami suporuotas ir nesuporuotas briaunas auginančiame
take.
Taip pat Θ gali būti efektyviau apskaičiuotas per O(n) vietoj O(𝑛2 ) turint atsilikimo (angl.
slack) kintamuosius ieškant augančio tako 2 žingsnyje. Ieškodami kiekvienai viršūnei uj,
1
slack(𝑢𝑗 ) = min∗ (𝑐𝑖𝑗 − α𝑖 ). Tada Θ = min(𝑠𝑙𝑎𝑐𝑘(𝑢𝑗 ). Šio Vengrų algoritmo sudėtingumas
𝑖∈𝐼 2 𝑗

O(𝑛3 ) priklauso būtent nuo šios implementacijos efektyvumo [GAM07].

12
2.2. Inrekementinis algoritmas
2.3. skyriuje aprašytas Dinaminis Vengrų algoritmas kaip pagrindą naudoja inkrementinės
priskyrimo problemos algoritmą [IG07], dėl to šiame poskyryje jis bus trumpai apžvelgtas.

2.2.1. Apibrėžimas
Inkrementinėje priskyrimo problemoje, turint svorinį dvidalį grafą ir jau turimą suporavimo
sprendimą, norime gauti maksimalų suporavimą tada, kai prie esamo grafo bus pridėta nauja
viršūnių pora, po viršūnę abiejuose grafo aibėse, kurios bus sujungtos briaunomis su visomis
kitomis kitos aibės viršūnėmis. Ši problema apima scenarijų, kai gali atsirasti papildomi
kintamieji, tačiau neapima realesnio varianto, kai esamų kintamųjų kainos (svoriai) gali pasikeisti.
Inkrementinis algoritmas pirmiausia apskaičiuoja naujas dualiųjų kintamųjų α𝑛+1 ir
β𝑛+1 reikšmes. Kiti dualūs kintamieji galutiniame sprendinyje vis dar yra teisingi. Ir tada
atliekama viena Vengrų algoritmo stadija, kad būtų rastas auginantis takas tarp naujų viršūnių v𝑛+1
ir u𝑛+1 . Jeigu bus atliekamas tik vienas pakeitimas, algoritmo sudėtingumas yra O(𝑛2 ).

2.2.2. Algoritmas
Įvestis: priskyrimo problemos dvidalis grafas {V, U, E} (kur |V| = |U| = n + 1) ir 𝑛 + 1 × 𝑛 +
1 svorio matrica; Optimalus sprendimas prieš tai buvusio 𝑛 × 𝑛 grafo su finalinėmis dualių
kintamųjų α𝑖 ir β𝑗 , kur i ∈ 1...n ir j ∈ i...n, reikšmėmis.
Išvestis: optimalus suporavimas M 𝑛 + 1 × 𝑛 + 1 problemai.

1) Inicializacija:

a) Pradedame duotu suporavimu M0 = M*.

b) Priskiriame galimas reikšmes dualiems kintamiesiems αn+1 ir βn+1 pagal taisykles:

β𝑛+1 = min ( min (𝑐𝑖(𝑛+1) − α𝑖 ), 𝑐𝑖(𝑛+1)(𝑛+1) )


1≤𝑖≤𝑛

α𝑛+1 = min (𝑐𝑖(𝑛+1)𝑗 − β𝑖 ),


1≤𝑗≤𝑛+1

2) Atliekame vieną Vengrų algoritmo stadijos iteraciją.

3) Grąžiname suporavimą M.

13
2.3. Dinaminis algoritmas
Šiame skyriuje aprašytas dinaminis Vengrų algoritmas, kuris apima problemą, kai turimo
dvidalio grafo briaunų svoriai keičiasi [GAM07].

2.3.1. Apibrėžimas
Šio algoritmo tikslas yra efektyviai pataisyti priskyrimo problemos sprendinį, kai turima
kainų matrica pasikeitė. Problema yra generelizuota inkrementinio priskyrimo versija, kurią
svarstė [IG07], kad galima būtų pritaikyti atvejams, kai briaunų svoriai keičiasi. Pagrindinė
algoritmo strategija yra sekti vietą, kurioje pasikeitę duomenys. Kai žinome, kad pasikeitė kurios
nors viršūnių briaunų kaina, tą viršūnę išimame iš galutinio turimo poravimo. Tada atnaujiname
dualių kintamųjų reikšmes pagal kitame skyriuje matomas formules ir praleidžiame vieną Vengrų
algoritmo stadiją.
Kadangi įvykdoma tik viena Vengrų algoritmo stadija, problemos sprendimo asimptotinis
sudėtingumas yra O(𝑛2 ). Dinaminis Vengrų algoritmas taip pat gali būti lengvai panaudojamas,
jeigu pasikeitė k skaičius stulpelių ar eilučių. Tada visos viršūnės yra išimamos iš galutinio
poravimo, atnaujinamos dualių kintamųjų reikšmės ir atliekamas Vengrų algoritmo stadijų k
skaičius. Tokiu atveju asimptotinis algoritmo sudėtingumas yra O(k𝑛2 ).

2.3.2. Algoritmas
Įvestis: priskyrimo problemos dvidalis grafas {V, U, E} (kur |V| = |U| = n) ir 𝑛 × 𝑛 svorio
matrica; Optimalus sprendimas prieš tai buvusio 𝑛 × 𝑛 grafo su pilnu suporavimu 𝑀∗ finalinėmis
dualių kintamųjų α𝑖 ir β𝑗 reikšmėmis; Eilutė ar stulpelis kur buvo atlikti pakeitimai.
Išvestis: optimalus suporavimas M naujai problemai su pasikeitusiais briaunų svoriais.

1) Inicializacija:

• Jei pasikeitė kainų matricos eilutė 𝑖 ∗

a) Iš suporavimo 𝑀∗ išimame briauną (𝑣𝑖 ∗ , 𝑝𝑜𝑟𝑎(𝑣𝑖 ∗ ));

b) Dualus kintamasis α𝑖 ∗ = min(𝑐𝑖 ∗𝑗 − β𝑗 ).


𝑗

• Jei pasikeitė kainų matricos stulpelis 𝑗 ∗

a) Iš suporavimo 𝑀∗ išimame briauną (𝑝𝑜𝑟𝑎(𝑢𝑗 ∗ ), 𝑢𝑗 ∗ );

b) Dualus kintamasis β𝑗 ∗ = min(𝑐𝑖𝑗 ∗ − α𝑖 ).


𝑗

2) Atliekame vieną Vengrų algoritmo stadijos iteraciją nurodytą 2.1.2;

14
3) Grąžiname suporavimą M.

2.3.3. Pavyzdys
Toliau galime pamatyti kaip veikia dinaminis algoritmas. Grafe suporuotos viršūnės
vaizduojamos pilnomis briaunomis, o nesuporuotos, tačiau leistinos viršūnės, taškinėmis
briaunomis. 5 pav. matome optimalų sprendinį, kurio suma yra 13.
Galime manyti, kad vieno iš stulpelių suma pasikeičia. Pagal algoritmo apibrėžimą viršūnės
poravimas sunaikinamas, tai galime pamatyti 6 pav. β4 reikšmė yra iš naujo apskaičiuojama pagal
2.3.2 skyriuje esančią formulę. Nauja reikšmė matoma 7 pav., kuri taip pat suteikia leistiną
briauną, matomą grafe v2, u4.
Dabar prasideda pagrindinis algoritmo žingsnis, kuris įvykdo vieną statinio Vengrų
algoritmo stadiją. Auginančio tako paieška prasideda tik iš v2. Kadangi takas nerandamas, reikia
atnaujinti viršūnių dualiųjų kintamųjų reikšmes, ką matome 8 pav. Tada takas ieškomas toliau ir
randamas (v4, u5, v6, u2, v5, u4). Esamas poravimas yra auginamas šiuo taku apkeičiant suporuotas
ir nesuporuotas briaunas, kaip matoma 9 pav. Galutinis optimalus poravimas, kurio suma 12,
matomas 10 pav. Pavyzdys sudarytas naudojant [GAM07].

5 pav. Turimas sprendimas su optimalia suma 12

15
6 pav. Pakeičiamas kainų matricos stulpelis

7 pav. Perskaičiuojamas dualus kintamasis β4

16
8 pav. Nerandamas auginantis takas, atnaujinamos reikšmės

9 pav. Esamas poravimas auginamas apkeičiant suporuotas ir nesuporuotas tako briaunas

17
10 pav. Galutinis grafo suporavimas

18
3. ALGORITMŲ PALYGINIMAS
Šiame skyriuje aprašomas reikalingų testavimui duomenų generatorius, kuris leidžia mums
greitai gauti didelius grafus ir atlikti norimas modifikacijas. Aprašoma pagrindinė aplinka, kurioje
buvo testuojami algoritmai, taip pat pagrindinės jų implementacijos detalės. Galiausiai
apžvelgsime pagal tam tikrus kriterijus palygintų algoritmų eksperimentų rezultatus.

3.1. Testavimo aplinka


Eksperimentų rezultatai priklauso ne tik nuo algoritmo, tačiau ir nuo infrastruktūros
parametrų bei programinės įrangos, kur tas algoritmas yra leidžiamas. Duomenų generatorius,
algoritmų palyginimo įrankis bei algoritmų implementacija yra įvykdyti Java programavimo
kalba.
Kompiuterio parametrai ant kurio vyko eksperimentai:
• Procesorius – Intel Core i5-8250U;
• RAM – 8.00GB DDR4;
• SSD tipo kietasis diskas;
• Operacinė sistema – Windows 10;
• Programavimo aplinka – IntelliJ IDEA;
• Java versija – 11.

3.2. Duomenys ir grafo vaizdavimas kompiuteryje


Norint daryti eksperimentus yra reikalingi duomenys, su kuriais galima būtų testuoti
algoritmus. Kad galima būtų daryti kokias nors išvadas, eksperimentų turėtų būti gana daug. Mūsų
atveju tam reikalingos grafo kainų matricos.
Kadangi grafas susideda iš kelių esminių dalių, t. y. viršūnių ir briaunų, kurios mūsų atveju
turi svorius, juos reikia kaip nors užrašyti, kad mūsų algoritmų palyginimo programa galėtų juos
nuskaityti.
Vienas iš populiariausių grafo užrašymo būdų yra gretimumo matrica arba sąrašas.
Gretimumo matrica vaizduoja viršūnes ir ar jos turi bendrą briauną, ar ne. Jeigu viršūnės
sujungtos, matricoje matome vienetą, o jeigu ne – nulį. Jeigu turime svorines briaunas, vietoj
vienetų įrašome tos briaunos svorį. Pavyzdį matome 12 pav. Taip pat kitas, galbūt geresnis būdas,
yra naudoti gretimumo sąrašą. Jie yra vaizduojami kaip sąrašų masyvas. Kadangi grafas
neorientuotas, užtenka pažymėti vieną viršūnių grupę. Iš pradžių apsirašome pagrindinę viršūnę,
o tada kitas viršūnes, su kuriomis ši viršūnė turi briauną ir tos briaunos svorį. Gretimumo sąrašą
matome 11 pav.

19
11 pav. Pavyzdinis gretimumo sąrašas

12 pav. Pavyzdinė gretimumo


matrica

Norint iš duotų duomenų sugeneruoti grafą, labiau yra patartina naudoti gretimumo sąrašą, nes
jo saugojimas užima mažiau vietos. Mūsų atveju naudosime paprastesnį gretimumo matricos
variantą – kainų matricą. Eilutės atitinka vieną dvidalio grafo aibę, stulpeliai kitą, o matricos
skaičiai – briaunų svorį jungiantį viršūnes.

3.3. Duomenų generavimas


Duomenų generavimui sukurta programa „Generator“. Programa, atitinkamai pagal gautus
parametrus, sugeneruoja kainų matricą. Parametrai, kurie gali būti programoje:
• nodeCount – int tipo kintamasis, kuris nustato koks bus generuojamos matricos
dydis;
• weightLimit – double tipo kintamasis, kuris nurodo maksimalų įmanomą briaunos
svorį, kai bus generuojama kainų matrica (minimalus - 1);
• fileName – failo pavadinimas kur norima, kad atsirastų nauja matrica arba pakeista;
• configFile – konfigūracijos failo pavadinimas, kuriame galima nurodyti papildomus
parametrus, tokius kaip direktoriją bei stulpelių numeriai, kuriuos norime
modifikuoti.
Programos galimybės:
• Matricos generavimas atitinkamai pagal paduotus argumentus;
• Matricos nuskaitymas iš norimos direktorijos ir failo;
• Matricos pakeitimai:
o Atsitiktinių briaunų svorių pakeitimas, kai norimų pakeisti briaunų kiekis
nurodomas;
o Atsitiktinių briaunų svorių pakeitimas, kai norimų pakeisti briaunų kiekis
nurodomas procentais;

20
o Stulpelio elementų pakeitimas, kai norimų pakeisti elementų kiekis stulpelyje
nurodomas procentais.
• Matricos generavimas, kurios briaunų svoriai yra visi unikalūs;
• Didelio kiekio skirtingų matricų generavimas ir pakeitimai, pagrindiniai parametrai
nurodomi programos viduje, nėra galimybės valdyti per konfigūracinius failus;
• Didelio kiekio skirtingų matricų generavimas į skirtingas direktorijas, pagrindiniai
parametrai nurodomi programos viduje.
Programa apima galimybę nesunkiai sugeneruoti norimą matricą ar atlikti pakeitimus
nežiūrint į programinį kodą. Tačiau, jei norima generuoti didelius matricų kiekius ar atlikti
pakeitimus, kur reikia nurodyti daug direktorijų ar failų, reikia pakeisti parametrus programos
funkcijų viduje.
Šio darbo eksperimentų generavimui naudota funkcija generateAdvancedSets, kuri, jos
viduje nurodžius direktorijas, nuskaito pagrindines matricas, pakeičia nurodytą elementų kiekį ir
išspausdina viską į failus. Atitinkamai nuo matricos dydžio bei pakeitimų skaičiaus, matricų
kiekis, kuris užima apie 60GB bei turi daug pakeitimų, šiuo kompiuteriu užėmė iki valandos laiko.
Galimi patobulinimai būtų visus parametrus iškelti į konfigūracinį failą iš kurio būtų
skaitoma.

3.4. Algoritmai bei testavimo aplinka


Kadangi mūsų algoritmai naudoja kainų matricą kaip pradinę įvestį, tai pirmas žingsnis, kurį
padaro palyginimo įrankis, tai grafo nuskaitymas ir pavertimas į dviejų dimensijų masyvą.
Kai turime kainų matricą, galime ją paduoti norimam algoritmui.
Modifikuoto Vengrų algoritmo implementacijai naudotas [Khu98] ir kaip pradinis taškas –
ši implementacija2.
Kadangi dinaminis Vengrų algoritmas veikia naudodamas statinio dalį, reikėjo atlikti kelis
pakeitimus į esamą implementaciją:
• Pridėta galimybė gauti visų kintamųjų reikšmes bei jas keisti;
• Pridėta galimybė išjungti kelias funkcijas naudojant boolean tipo kintamąjį, nes
dinaminis algoritmas naudoja gautą sprendinį ir jo reikšmes;
• Galimybė panaikinti poravimą tarp tam tikrų viršūnių.
Dinaminis Vengrų algoritmas implementuotas naudojantis [GAM07] bei ten aprašytomis
taisyklėmis. Pagrindiniai DynamicHungarian klasės veikimo principai, kurie taip pat aprašyti 2.3.2
skyriuje:

2
Implementacija - https://github.com/KevinStern/software-and-algorithms/blob/master/
src/main/java/blogspot/software_and_algorithms/stern_library/optimization/HungarianAlgorithm.java
21
1. Gauname statinio Vengrų algoritmo objektą, originalią kainų matricą bei kainų
matricą su atliktais joje pakeitimais;
2. Atnaujiname Vengrų algoritmo objekto kainų matricą į naują;
3. Patikriname skirtumus tarp senos kainų matricos ir naujos. Randame pasikeitusias
vietas (šiuo atveju stulpelius) ir reikiamose vietose sunaikiname poravimus;
4. Pagal straipsnyje [GAM07] aprašytas taisykles atnaujiname dualius kintamuosius;
5. Paleidžiame statinį Vengrų algoritmą nusiųsdami reikšmę false, kad nebūtų sugadinti
pradinio sprendinio parametrai.
Pagrindinė programos klasė Main turi galimybę atlikti tokius veiksmus:
• Nuskaityti matricą iš nurodytos vietos;
• Praleisti didelį kiekį testų statiniam Vengrų algoritmui;
• Praleisti didelį kiekį testų statiniam bei dinaminiam Vengrų algoritmams. Gautus
duomenis apie laiką bei kitus norimus parametrus (kaip kad kurios viršūnės
suporuotos su kuriomis) išsisaugoti bei išspausdinti į norimą vietą (ekraną arba failą);
• Galimi du išspausdinimo būdai:
1. Išspausdinama į nurodytą failą ar failus suprantamais žodžiais bei
komentarais apie gautus parametrus po algoritmų suveikimo;
2. Išspausdinama į nurodytą failą ar failus gautus duomenis, kad būtų galima
implementacija su kitomis programomis.
Kad gautume kuo tikslesnį algoritmo įvykdymo laiką, naudojame Java funkciją
System.nanoTime(). Taip pat laiką skaičiuojame nuo tada, kai pradeda veikti algoritmas, o ne kai
sukuriamas klasės objektas bei priskiriami kintamieji.
Galiausiai atėmę iš dabartinio laiko pradinį ir padalinę iš milijono gauname apytikslį laiką
milisekundėmis, kiek laiko užtruko algoritmai spręsdami užduotį.

3.5. Eksperimentai
Eksperimentai atlikti norint gauti kuo tikslesnius rezultatus valdant tam tikrus parametrus.
Pagrindiniai eksperimentų parametrai buvo:
• Stulpelių pakeitimo skaičius;
• Stulpelio elementų pakeitimo skaičius;
• Svoriai skiriami briaunoms kainų matricoje;
• Viršūnių skaičius.
Kaip matome N pav. naudojama viršūnių skaičių aibė buvo sudaryta iš 7 elementų.
Kiekvienam viršūnių skaičiui buvo sugeneruojami keturi pradiniai failai, kurie sekė tokias
taisykles:

22
𝑛 𝑛
• File1 – matrica dydžio × , kur n – bendras viršūnių skaičius, briaunų svoriai tarp
2 2

1 ir 10;
𝑛 𝑛
• File2 – matrica dydžio × , kur n – bendras viršūnių skaičius, briaunų svoriai tarp
2 2

1 ir 100;
𝑛 𝑛
• File3 – matrica dydžio × , kur n – bendras viršūnių skaičius, briaunų svoriai tarp
2 2

1 ir 1000;
𝑛 𝑛
• File4 – matrica dydžio × , kur n – bendras viršūnių skaičius, briaunų svoriai visi
2 2

unikalūs.

Turint pagrindinius failus, galima atlikti reikiamus pakeitimus. Atlikti pakeitimai buvo pagal
šias taisykles, kur N – viršūnių skaičius:

• N_changed_1 – pakeisti visi vieno atsitiktinio stulpelio matricos elementai (𝑢𝑗 );


• N_changed_1_random – pakeistas atsitiktinis skaičius atsitiktinio stulpelio matricos
elementų (𝑢𝑗 );
• N_changed_10 – pakeistas atsitiktinis skaičius atsitiktinių 10-ties stulpelių matricos
elementų (𝑢𝑗 );
• N_changed_10_random – pakeistas atsitiktinis skaičius 10-ties atsitiktinių stulpelių
matricos elementų (𝑢𝑗 );
• N_changed_n – kiekvieną kartą keičiant matricą pakeičiama vienu daugiau matricos
stulpeliu. Sugeneruojama tiek failų, kiek yra stulpelių 𝑢𝑗 ;
• N_changed_n_random – kiekvieną kartą keičiant matricą pakeičiama vienu daugiau
matricos stulpeliu. Pakeičiamas atsitiktinis stulpelio elementų skaičius.
Sugeneruojama tiek failų, kiek yra stulpelių 𝑢𝑗 .

Kaip pavaizduota 13 pav., kiekvienam viršūnių skaičiui gaunama daugiau nei keturi
tūkstančiai failų, paskutinių dviejų taisyklių sugeneruoti failai priklauso nuo matricos dydžio.

23
13 pav. Eksperimentams reikalingų kainų matricų failų direktorijų struktūra

Atliekant eksperimentus vadovautasi pagrindiniais statistikos dėsniais ir formulėmis. Viena


pirmųjų eksperimentų užduočių buvo išsiaiškinti, kaip pasiskirsto algoritmo veikimo laikai. Tam
panaudotas vidurkio bei standartinio nuokrypio skaičiavimas, kurį vėliau galima panaudoti norint
gauti normaliojo skirstinio kreivę.
Normaliojo skirstinio kreivę galima pamatyti 14 pav. Grafike galima pamatyti kaip tankiai
yra pasiskirstę duomenys. 68.2% duomenų yra tarp vieno standartinio nuokrypio nuo vidurkio.
Šiuo atveju žiūrint į statinio Vengrų algoritmo su nepasikeitusia matrica kreivę, jos standartinis
nuokrypis yra 5.11, tai reiškia, kad palyginus su Vengrų algoritmo kreive, kur matrica pasikeitusi,
duomenys yra mažiau nukrypę nuo vidurkio.
Ką galime pastebėti, kad nors buvo atlikta 1000 pakartotinų bandymų ant tos pačios matricos
naudojant statinį Vengrų algoritmą, duomenys nėra visiškai vienodi. Taigi galime teigti, kad
algoritmų laikams daro įtaką pati kompiuterio aplinka arba jos „triukšmas“.
Identiškas kreives galima pamatyti beveik visose normaliojo skirstinio kreivėse, kur veikiant
algoritmui nebuvo itin didelio pašalinio triukšmo. Kodėl su pasikeitusia matrica duomenys yra
mažiau tankesni, nes tam daro įtaką ne tik kompiuterio foninis triukšmas, tačiau ir pakeitimai

24
daryti matricai. Padidinti ar sumažinti svoriai, nors ir kelių stulpelių, gali truputį sulėtinti arba
pagreitinti algoritmo veikimą, kuris, turint pakankamai duomenų, matosi grafike.

Normalusis skirstinys
0.09
0.08
0.07
0.06
0.05
0.04
0.03
0.02
0.01
0
0 5 10 15 20 25 30 35 40 45 50
Standartinis nuokrypis

Statinis Vengrų algoritmas Statinis Vengrų algoritmas su pasikeitusia kainų matrica

14 pav. Normalusis skirstinys 2048 viršūnių, File1. Statinio Vengrų


algoritmo vidurkis yra 23.91ms, standartinis nuokrypis 5.11, statinio Vengrų
algoritmo vidurkis su pasikeitusia kainų matrica yra 23.91ms, standartinis
nuokrypis 6.41.

Kitas eksperimentas parodo laiką, kai poravimui naudojamas statinis Vengrų algoritmas, o
matricai pasikeitus, kad pataisyti sprendinį, poravimui iš naujo naudojamas dinaminis Vengrų
algoritmas. Gautus rezultatus matome 15 pav. Grafo briaunų svoriai vyrauja tarp 1 ir 100. Grafikas
vaizduojamas naudojant linijinę skalę, o 16 pav. matomas grafikas, kuris naudoja logaritminę
skalę. Logaritminiame atvaizdavime galime aiškiau pamatyti laiko skirtumus, kiek greičiau yra
naudoti dinaminį Vengrų algoritmą, kai pasikeičia nedidelis duomenų kiekis, bet pačių viršūnių
skaičius yra didelis.

25
Poravimo ir suporavimo iš naujo laikas (ms)
1200.00

1000.00

800.00
Laikas (ms)

600.00

400.00

200.00

0.00
64 128 256 512 1024 2048 4096
Viršūnių skaičius

0.05 Poravimas iš naujo (1 stulpelis) Poravimas iš naujo (10 stulpelių)

15 pav. Poravimas ir suporavimas iš naujo naudojant dinaminį Vengrų


algoritmą

Poravimo ir suporavimo iš naujo laikas (ms)


1000.00

100.00
Laikas (ms)

10.00

1.00
64 128 256 512 1024 2048 4096
0.10

0.01
Viršūnių skaičius

Poravimas Poravimas iš naujo (1 stulpelis) Poravimas iš naujo (10 stulpelių)

16 pav. Poravimas ir suporavimas iš naujo naudojant dinaminį Vengrų


algoritmą, kai ordinačių ašis yra logaritminė

26
Iš rezultatų taip pat galima matyti, kad vis daugėjant pasikeitusių stulpelių skaičiui,
dinaminio Vengrų algoritmo laikas išauga. Žiūrint į visus duomenis gali matytis tendencija, kad
dinaminį algoritmą normaliu atveju apsimoka naudoti, kai pasikeitusių stulpelių skaičius neviršija
70% visų stulpelių.

Poravimo ir suporavimo iš naujo laikas (ms)


10
9
8
7
Laikas (ms)

6
5
4
3
2
1
0
0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 256
Stulpelių skaičius

Poravimas Suporavimas iš naujo (pasikeitusių stulpelių skaičius auga)

17 pav. Kreivės rodo poravimą bei suporavimą iš naujo, kai vis auga besikeičiančių
stulpelių skaičius. Viršūnių skaičius 512, maksimalus svoris 10000

Poravimo ir suporavimo iš naujo laikas (ms)


80.00
70.00
60.00
Laikas (ms)

50.00
40.00
30.00
20.00
10.00
0.00
0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1024
Stulpelių skaičius

Poravimas Suporavimas iš naujo (pasikeitusių stulpelių skaičius auga)

18 pav. Kreivės rodo poravimą bei suporavimą iš naujo, kai vis auga besikeičiančių
stulpelių skaičius. Viršūnių skaičius 2048, maksimalus svoris 10000

27
Taip pat turint algoritmų veikimo laikus buvo pastebėta, kad su tam tikrais svoriais
statinis algoritmas užtrunka žymiai ilgiau. Tai galime pamatyti 19 pav., kur viršūnių skaičius
1024, o maksimalūs galimi svoriai 100 ir 10000. Pagal statinio Vengrų algoritmo
implementaciją briaunų svoriai neturėtų turėti jokios reikšmės. Tai patikrinta paprasčiausiai
padalinus kainų matricą iš tam tikro svorio.
Nors atrodo, kad jeigu maksimalūs galimi svoriai grafe būtų 100 ir 10000, tai tik pradėjus
algoritmui veikti ir padalinus antrojo grafo kainų matricą iš 100 turėtume gauti tokius pačius
rezultatus, dėl to svoriai turėtų reikšmę. Tačiau padalinus laikai nepasikeičia ir pirmojo grafo
veikimo laikas išlieka žymiai ilgesnis.
Taip pat pažvelgus į algoritmo implementaciją matoma, kad kainų matrica yra double
kintamojo tipo, dėl to padalinus kainų matricą unikalių reikšmių kiekis nesumažėja (duplikatų
skaičius nepadidėja).

Statinio Vengrų algoritmo veikimo laikas (ms)


300.00

250.00

200.00
Laikas (ms)

150.00

100.00

50.00

0.00
0 100 200 300 400 500 600 700 800 900
Eksperimentų skaičius

Maksimalus briaunos svoris 100 Maksimalus briaunos svoris 10000

19 pav. Statinio Vengrų algoritmo veikimo laikas, kai briaunų svoriai 100 bei
10000. Grafo viršūnių skaičius 1024

Norint patikrinti pastebėtus dalykus reikalingi duomenys, kurie parodytų priklausomybę nuo
grafo dydžio ir briaunų svorio duplikatų skaičiaus. Taip pat galima pažiūrėti, kuri būtent vieta
algoritme atlieka daugiau pakartojimų, kai algoritmo laikas išauga.

28
Statinio Vengrų algoritmo veikimas
100.00 30000

"Slack" kintamųjų atnaujinimo kiekis


90.00
80.00 25000
70.00 20000
Laikas (ms)
60.00
50.00 15000
40.00
30.00 10000
20.00 5000
10.00
0.00 0
10
50
100
200
300
400
500
600
700
850
1000
1200
1500
1700
2500
3500
4500
6000
8000
10000
20000
30000
40000
75000
125000
175000
Maksimalūs svoriai

Vengrų algoritmo veikimo laikas "Slack" kintamųjų atnaujinimo kiekis

20 pav. Statinio Vengrų algoritmo veikimo laikas ir „Slack“ kintamųjų


atnaujinimo kiekis priklausomai nuo maksimalaus grafo svorio. Grafo viršūnių skaičius
1024

Statinio Vengrų algoritmo veikimas


4500.00 450000

"Slack" kintamųjų atnaujinimo kiekis


4000.00 400000
3500.00 350000
3000.00 300000
Laikas (ms)

2500.00 250000
2000.00 200000
1500.00 150000
1000.00 100000
500.00 50000
0.00 0
10
50
100
200
300
400
500
600
700
850
1000
1200
1500
1700
2500
3500
4500
6000
8000
10000
20000
30000
40000
75000
125000
175000

Maksimalūs svoriai

Vengrų algoritmo veikimo laikas "Slack" kintamųjų atnaujinimas

21 pav. Statinio Vengrų algoritmo veikimo laikas ir „Slack“ kintamųjų


atnaujinimo kiekis priklausomai nuo maksimalaus grafo svorio. Grafo viršūnių skaičius
4096
Kaip matome 20 ir 21 pav. pavaizduotose kreivėse, maksimalus galimas grafo briaunos
svoris turi įtakos algoritmo veikimo greičiui. Didesnis svoris leidžia grafui turėti daugiau unikalių
ir mažiau pasikartojančių reikšmių. Tai pat matome priklausomybę tarp algoritmo veikimo laiko
ir algoritmo vietos, kur yra atnaujinami „Slack“ kintamieji.

29
Vengrų algoritmo "Slack" kintamųjų atnaujinimas
10000000.00
1000000.00
100000.00
10000.00
1000.00
100.00
10.00
1.00
10
50
100
200
300
400
500
600
700
850
1000
1200
1500
1700
2500
3500
4500
6000
8000
10000
20000
30000
40000
75000
125000
175000
Briaunų maksimalus svoriai

512 1024 2048 4096 8192

22 pav. Vengrų algoritmo „Slack“ kintamųjų atnaujinimo kiekis priklausomai


nuo maksimalaus grafo svorio. Vaizduojami grafai su skirtingų viršūnių dydžiu
logaritminė skalė
22 pav. galime pamatyti, kad didesniam grafui yra optimalu turėti didesnį skaičių galimai
unikalių reikšmių. Tačiau galime pamatyti, kad esant labai mažam svoriui, algoritmas taip pat
veikia greičiau. Vienas iš paaiškinimų būtų, kad turint didelį grafą su mažais svoriais, nors
duplikatų skaičius ir didelis, pradinis algoritmo žingsnis, kuris bando primityviu būdu ieškoti
tobulo poravimo, eidamas pro matricą kiekvienoje eilutėje randa pakankamą mažo svorio
elementų kiekį, iš kurių galima padaryti beveik pilna tobulą poravimą.
Tai rodo duomenys, kuriuose matyti, kad turint mažus svorius, nors grafas ir didelis,
auginantis takas randamas labai mažą kiekį ir toliau tik kyla 23 pav.

Auginantis takas
1000
Auginančio tako radimo kiekis

900
800
700
600
500
400
300
200
100
0
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51

Auginantis takas

23 pav. Grafo su 8192 viršūnėmis auginančio tako radimo kiekis

30
Žinant šias priklausomybes galima implementuoti algoritmą, kuris turėdamas du vienodo
dydžio grafus bei pirmojo grafo sprendimą M galėtų pats parinkti, kokiu būdu greičiau galima
išspręsti priskyrimo uždavinį grafe.

3.6. Algoritmas
Algoritmo implementavimas turi būti sudarytas iš pasirinkimo, kada geriau naudoti vieną ar
kitą algoritmą. Konstruojant algoritmą turime kelis apribojimus:
• Gaunami du grafai G1 ir G2, kurie yra vienodo dydžio ir kurie gali turėti tokį patį
unikalių elementų kiekį (sutampa svoriai);
• Turime grafo G1 sprendinį.
Atsižvelgiant į prieš tai gautus rezultatus galime padaryti tokias sąlygas, pagal ką bus
renkamasi, kurį sprendimą pasirinkti, statinį ar dinaminį Vengrų algoritmą:
• Jeigu viršūnių skaičius mažiau negu 512, visuomet sprendžiame statiniu algoritmu,
nes laikas neturėtų viršyti 1 milisekundės;
• Atsižvelgiant į 22 pav. galime pasidaryti tokias taisykles, kad kai briaunų
maksimalus galimas svoris yra daugiau nei 2% bei mažesnis nei 25% grafo viršūnių
skaičiaus, visuomet sprendžiame dinaminiu Vengrų algoritmu pasitelkdami turimą
G1 grafo sprendimą;
• Jei buvusios sąlygos nepatenkintos, patikriname kiek stulpelių atnaujinta G2 grafe
palyginti su G1. Jeigu stulpelių atnaujinta daugiau negu 80%, sprendžiame
naudodami statinį Vengrų algoritmą, jeigu mažiau, naudojame dinaminį.
Implementavus tokį „Solution“ algoritmą, kuris yra paprastas, tačiau paremtas prieš tai
gautais rezultatais, veikimą galime pamatyti 24 pav. Žiūrint į duomenis pirmiausia patenkinama
sąlyga, kad veiktų dinaminis Vengrų algoritmas, o tada, pasiekus tam tikrą ribą, pradeda veikti
statinis. Patenkinus algoritmo pirmąją ar antrąją sąlygas turėti G1 algoritmo sprendinio nebūtina.

31
Poravimo ir suporavimo iš naujo (ms)
80.00
70.00
Laikas (ms) 60.00
50.00
40.00
30.00
20.00
10.00
0.00
0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1024
Stulpelių skaičius

Poravimas
Suporavimas iš naujo (pasikeitusių stulpelių skaičius auga)
Poravimas naudojant Solution algoritmą

24 pav. Grafo su 2048 viršūnėmis veikimo laikai, kai naudojame statinį Vengrų
algoritmą, dinaminį ir naują „Solution“ algoritmą. Maksimalus grafo svoris 10000

32
REZULTATAI IR IŠVADOS

Šiame darbe aprašyti ir palyginti du priskyrimo problemai skirti spręsti algoritmai, kurie
naudoja grafus bei jų poravimą. Vienas iš jų, tai klasikinis Vengrų algoritmas, o kitas,
dinaminis Vengrų algoritmas, kuris skirtas padidinti efektyvumą sprendžiant uždavinį, kada
keičiasi grafo briaunų svoriai.
Sukurta testavimo aplinka, kuri leido greitai generuoti didelius grafų matricų kiekius bei
atlikti pakeitimus, tokius kaip stulpelių pakeitimai. Gavus didelį duomenų kiekį buvo galima
daryti eksperimentus, kurie davė rezultatus apie algoritmų veikimo greičius bei nuo ko tai
priklauso.
Implementuoti ir ištestuoti algoritmai leido gauti rezultatus apie tai, kokiomis sąlygomis
sparčiau veikė statinis, o kada dinaminis Vengrų algoritmas. Gauti rezultatai apie tai, kaip
keičiasi algoritmo veikimo greitis, kai yra keičiamas įvairus jo stulpelių kiekis. Taip pat
matoma kaip algoritmo spartai daro įtaką unikalių svorių kiekis.
Duomenys rodo, kad nors svoriai kaip skaičiaus dydis algoritmo nepaveikia, tačiau svoris
nurodo kiek grafo matricoje gali būti unikalių elementų. Didesnis svoris leidžia grafe atsirasti
didesniam kiekiui unikalių elementų, kurie, priklausomai nuo grafo dydžio, gali daryti didelę
įtaką veikimo greičiui.
Taip pat gauti rezultatai leido sukurti algoritmą, kuris turėdamas informaciją apie grafo
dydį bei unikalių elementų kiekį gali parinkti, kada taikyti statinį, o kada dinaminį Vengrų
algoritmą. Tačiau šis parinkimas nėra universalus, prieš nustatant tam tikras ribas reikėtų atlikti
testus aplinkoje, kur būtų naudojamas toks algoritmas. Gauti rezultatai apie veikimo greitį su
tam tikru kiekiu unikalių elementų galėtų žymiai padidinti algoritmo tinkamo pasirinkimo
tikimybę.
Rezultatai leidžia teigti, kad dinaminis Vengrų algoritmas, turint gana didelį grafą, kai
viršūnių dydžiai viršija tūkstantį bei atliekamas tik kelių vienetų grafo briaunų svorių
pakeitimas, veikia ženkliai greičiau nei statinis.
Taip pat matoma vienodų elementų grafe įtaką veikimo greičiui. Kai briaunų maksimalus
svoris sveikais skaičiais viršija 10, tačiau neviršija apie trečdalį viršūnių skaičiaus, gana stipriai
suprastėja algoritmo veikimo greitis, tačiau ši įtaka bei ribos, kuomet suprastėja algoritmo
veikimo greitis taip pat priklauso ir nuo grafo dydžio.
Taigi, turint gana didelį grafą bei žinant, kad ten bus atliekamas gana mažas pakeitimų
skaičius, galima gauti pirmą optimalų poravimą naudojant statinį Vengrų algoritmą, o tada

33
pasirinkti dinaminį. Tai ženkliai padidins optimalaus sprendinio gavimą, nei kad būtų spręsti
uždavinį iš naujo naudojant statinį algoritmą.

34
ŠALTINIAI

[AB09] S. Arora, B. Barak. „Computational Complexity“. Cambridge University


Press, Cambridge, 2009.
[BM76] J. A. Bondy, U. S. R. Murty. „Graph Theory With Applications“. The·Macmillan
Press Ltd, New York, 1976.
[CK98] Christos H. Papadimitriou, Kenneth S. „COMBINATORIAL
OPTIMAZATION Algorithms and Complexity“. Dover Publications, 1998.
[EGG98] D. Eppstein, Z. Galil, G. F. Italiano. „Handbook of Algorithms and Theory of
Computation“, Chapter 8. CRC Press, 1999.
[GAM07] G. Ayorkor Mills-Tettey, Anthony Stentz, M. Bernardine Dias. „The
Dynamic Hungarian Algorithm for the Assignment Problem with Changing Costs“. Tech. Report,
CMU-RI-TR-07-27, Robotics Institute, Carnegie Mellon University, July, 2007.
[IG07] Ismail H. Toroslu, Göktürk Üçoluk. „Incremental assignment problem“.
Information Sciences 177, 6 (March 2007), 1523–1529.
[Khu98] S. Khuller. „Design and Analysis of Algorithms: Course Notes“.
http://hdl.handle.net/1903/592, 1998.
[Kuh56] H. W. Kuhn. „The Hungarian Method for the Assignment Problem“. Naval
Reasearch Logistics Quarterly, 1956, 712-726.
[Mun57] J. Munkres. "Algorithms for the Assignment and Transportation Problems".
Journal of the Society for Industrial and Applied Mathematics, 5(1), 1957 March, 32–38.

35

You might also like