Amorized Analysis en Union-Find Algoritmiek Vandaag • Amortized analysis – Technieken voor tijdsanalyse van algoritmen • Union-find datastructuur – Datastructuur voor operaties op disjuncte verzamelingen – Verschillende oplossingen – Analyse van uiteindelijke oplossing – Toepassing 2 Algoritmiek Tijdsanalyse van een algoritme • Soms: analyse van controlestructuur – Kijken naar geneste loops, etc. • Hier: een data-structuur met operaties – Ge-amortiseerde tijd: hoeveel tijd kost elke operatie gemiddeld over alle operaties. • Drie technieken: – Aggregate method – Accounting method – Potential method 3 Algoritmiek Probleemstelling • Data-structuur • Verzameling operaties op een data-structuur • Wat is de gemiddelde tijd in het slechtste geval per operatie? – Worst case average time: amortized time 4 Algoritmiek Aggregate method • Bereken totale tijd T(n) over een serie van n operaties op de data-structuur. • Amortized time is T(n)/n. • Voorbeelden: – Stack met multipop – Binare teller – Dynamische array 5 Algoritmiek Stack met multi-pop • Stack met drie operaties: – Push(S,x): zet x bovenop de stack – Pop(S): haal bovenste element van stack en lever dat op – Multipop(S,k): haal de bovenste k elementen van de stack, of maak stack leeg als deze minder dan k elementen heeft: • Multipop(S,k) while not(Stack-empty(S)) and k 0 do Pop(S); k = k – 1; 6 Algoritmiek Aggregate analysis voor de stack met multipop • Worst-case tijd van multipop is O(n). • Neem aan dat we beginnen met een lege stack. • Amortized tijd van operaties is … O(1), want: – Stel we doen n operaties op de stack. – Dus doen we hooguit n keer een Push – Dus kunnen we hooguit (minder dan) n keer een element Pop-en – Totale tijd over alle Pop- en Multipop-operaties: O(n) – Totale tijd: O(n) – Amortized time van operatie: O(n/n) = O(1) 7 Algoritmiek Verhogen van een binaire teller • Array A[0…k – 1] van bits • Initieel 000…00 • Increment(A) i = 0; While i < length(A) and A[i] = 1 Do A[i] = 0; i ++ If i < length(A) then A[i] =1 8 Algoritmiek • • • • • • • • • • • • • 0 1 2 3 4 5 6 7 8 9 10 11 12 000000 000001 000010 000011 000100 000101 000110 000111 001000 001001 001010 001011 001100 0 1 3 4 7 8 10 11 15 16 18 19 20 Amortized analyse van binaire teller • Een enkele operatie kan O(k) tijd kosten. • Amortized: – Elke operatie zet slechts 1 bit van 0 naar 1, maar misschien meerdere bits van 1 naar 0. – Als we n Increment-operaties doen: • Maximaal n keer bit van 0 naar 1. • Dus ook maximaal n keer bit van 1 naar 0. • Totaal O(n) werk – O(1) amortized tijd van increment. 9 Algoritmiek Dynamische array • Array: – Start op formaat 10 – Toevoegen van element kan totdat array vol is (meestal O(1) werk, tenzij array vol) – Als array vol: • formaat = formaat * 2 • Maak nieuwe array met grootte formaat • Kopieer oude array naar nieuwe array • Kost O(formaat) tijd 10 Algoritmiek Aggregate voor dynamische array • Als we n inserts doen, dan: – O(n) voor gewone inserts: – Stel laatste verdubbeling gebeurde bij formaat k – Totale tijd van verdubbelingen: • O(10+20+40+ … + k ) = O(k) • n <= 2k • Dus O(n) totaal, dus O(1) amortized 11 Algoritmiek De accounting method • Iedere operatie betaalt hypothetische kosten. • Daarmee: – Betaalt hij voor de acties die hij doet – Kan hij credit op objecten in de datastructuur plaatsen – Kan hij eventueel betalen met credit dat uit de datastructuur gehaald wordt 12 Algoritmiek Accounting method Stack met multipop • Operaties kosten: – Push: 1 – Pop: 1 – Multipop: min(k, |S|) • Geef geamortiseerde kosten: – Push: 2. Zet 1 credit op element in S. – Pop: 0. Betaal actie met credit. – Multipop: 0. Betaal actie met credit – Invariant: elk element in S heeft 1 credit. 13 Algoritmiek Binaire teller met accounting • Array A[0…k – 1] van bits • Initieel 000…00 • Increment(A) i = 0; While i < length(A) and A[i] = 1 Do A[i] = 0; i ++ If i < length(A) then A[i] =1 14 • Laat elke increment 2 kosten. • Zet 1 credit op de 1 die we gemaakt hebben. • Betaal zetten van 1 naar 0 met credits op de 1-en. • O(1) amortized. Algoritmiek Dynamische array met accounting • Als we een element toevoegen, dan: – Geef 1 credit aan een element dat nog geen credit heeft – Geef 1 credit aan element zelf • Als we verdubbelen, dan heeft elk element precies 1 credit • Dus O(1) amortized 15 Algoritmiek Potentiaal methode • Potentiaal-functie F: Datastructuur reals • Als D0 initiele datastructuur en Dj datastructuur na j operaties, dan F(Dj)F (D0) • Geamortiseerde kosten van een operatie i kunnen we nemen als: – Echte kosten ci plus F(Dj) – F(Dj –1) – Merk op: totale kosten is minstens som van geamortiseerde kosten. 16 Algoritmiek Potentiaal methode voor multipop • Potentiaal van stack is aantal elementen op stack. • Initieel 0; dus altijd minstens potentiaal van initiele datastructuur. • Geamortiseerde kosten Push: 2. (1 voor operatie en 1 voor toename potentiaal). • Geamortiseerde kosten Pop: 0. (1 voor operatie en –1 wegens potentiaalverschil.) • Geamortiseerde kosten Multipop: 0 (min(k,|S|) voor operatie en – min(k,|S|) wegens potentiaalverschil.) 17 Algoritmiek Potentiaalmethode voor binaire teller • Potentiaal van bitstring: aantal bits dat 1 is. • Initieel 0; dus altijd minstens potentiaal van initiele datastructuur. • Geamortiseerde kosten van operatie zijn hooguit 2. 18 Algoritmiek Potentiaalmethode voor dynamische array • Stel we hebben een array met formaat f waarin n elementen zitten • Geef deze potentiaal 2n-f • Elke gewone invoeging doet potentiaal met 2 stijgen • Verdubbeling: we doen dit als n=f, dus potentiaal is 2n-n = n, en na afloop is potentiaal 0, dus we betalen de operatie met het potentiaalverschil 19 Algoritmiek Union-find datastructuur • Probleemstelling: datastructuur voor disjuncte verzamelingen met operaties – Vereniging (Union) – Zoeken (Find) • Verschillende datastructuren voor dit probleem • Analyse van de verschillende datastructuren • Uiteindelijke oplossing: `union by rank’ en padcompressie • Toepassingen – O.a. implementatie voor algoritme voor schedulingprobleem 20 Algoritmiek Disjuncte verzamelingen • Partitie van universum U in aantal disjuncte deelverzameling van U. – Elk element van U zit in precies een verzameling in de collectie a b d c e g f U U = { {a,b,c} , {d,e,g}, {f} } 21 Algoritmiek Een ADT voor disjuncte verzamelingen I: Create • Verschillende operaties – Create – Union – Find a b f U 22 a d c Create ( v ) Voegt nieuwe verzameling aan de collectie toe met element v Wordt aangeroepen als v niet al in de collectie zit. e b d c g f U’ v Algoritmiek e g II: Union Vereniging • Union (S1, S2) – Wordt aangeroepen met naam / objectverwijzing / identificatie van twee verzamelingen – Vervangt de verzamelingen S1 en S2 in de collectie door een verzameling S1 S2 23 Algoritmiek a S1 c h S2 b d g e f a v b d c h v f e g III: Find • Find (v) • Levert de naam / objectverwijzing / identificatie op van de verzameling waar v in zit. • Iedere verzameling heeft dus unieke manier om geïdentificeerd te worden – Bijv. naam van een uniek element uit de verzameling 24 Algoritmiek a S1 c h S2 v b d f e g Find(a) geeft een verwijzing naar S1. Incrementeel samenhangende componenten • Graaf G. • Operaties: • Voeg een kant toe tussen twee knopen x en y • Geef de knopen in de samenhangende component van x • Zitten x en y in dezelfde samenhangende component 25 Addedge(x,y) S1 = Find(x) S2 = Find(y) if S1 S2 then Union(S1,S2) Algoritmiek Toepassing Hoe implementeren we een datastructuur voor `union-find’? • Verschillende implementaties • Soms willen/kunnen we de datastructuur met extra operaties uitbreiden (zie werkcollege). • Operaties moeten vlot gaan 26 Algoritmiek Heel simpel idee: array • Houdt bij elk element bij de naam van de verzameling waar deze in zit: set(v) • Find: O(1). • Union (S, T): – Vervelend: • Of het hele array langsgaan en voor alle elementen kijken of ze in S en T zitten • Of we hebben voor verzamelingen S en T opgeslagen welke elementen erin zitten: – Maar dan hebben we de array niet zo nodig • Create: lastig, want hoe lang moet de array worden? 27 Algoritmiek Datastructuur met lijsten • Een verzameling wordt gerepresenteerd met een lijst van zijn elementen; elk element heeft een pointer naar zijn representatie (bijv.: 1e element). 28 a b c d a b c d Algoritmiek e e f f g g Verbetering 1 • 1e lijst moet doorgelopen worden, alleen om laatste element te vinden om pointer naar 1e element van 2e lijst te maken a b c d a b c d Houdt dus ook een pointer naar29het laatste element bij e e f f g g O(1) extra werk aan deze pointer Algoritmiek Verbetering 2 • Als we pech hebben / dom zijn, wordt grote lijst achter kleine lijst gezet e e a b c d a b c d Dus … 30 Algoritmiek Union by size • Houdt van elke verzameling ook het formaat bij. • Bij een vereniging: – Zet de kleinste lijst achter de grootste – Tel de formaten op. • Hoeveel tijd kost dit? – Hoeveelheid tijd is • O(1) voor find • O(1) voor create • O(1+ lengte kortste rij) voor union: hoeveel is dat nu precies? 31 Algoritmiek Analyse union by size • Stel element x zit in verzameling met formaat n1 en union wordt gedaan met verzameling met formaat n2. – Als n1 n2, dan moet de pointer naar het eerste element worden omgezet, en zit x na afloop in een verzameling met n1 + n2 2*n1 elementen. – Anders wordt de pointer naar het eerste element niet omgezet. • Als |U|=n, dan kan een element maar hooguit log n keer in een minstens twee keer zo grote verzameling komen. • Dus: totale hoeveelheid werk van het omzetten van pointers over alle union-operaties is O(n log n). – O(log n) gemiddeld per union. 32 Algoritmiek Tijdgrenzen lijsten met union by size • Find: O(1) • Create: O(1) • Union: – O(n) worst case – O(log n) geamortiseerd 33 Algoritmiek Union-find met snelle union • Duur was: omzetten van alle pointers naar het 1e element. • Dus, laten we dat dan niet doen. 34 a b c d a b c d e Zoeken gaat in meerdere stappen Algoritmiek e f f g g Observaties • Zoeken: ga een aantal pijlen naar voren af, tot je aan begin van de lijst gekomen bent. • De lijst hoeft geen lijst meer te zijn: we gebruiken de lijst niet meer. 35 a b c d e f g a b c d e f g Algoritmiek Boomstructuur • Een verzameling wordt gerepresenteerd door een boom. • Pijlen wijzen omhoog, naar wortel. Identiteit van verzameling is / wordt opgeslagen in wortel van boom. a a b c d e f g c 36 e b Algoritmiek d f g Find in boomstructuur Find (x) r = x; while parent(r) r do r = parent r Return informatie opgeslagen bij r a e b c 37 Algoritmiek d f g Union in boomstructuur • Hang kleinste boom onder grootste boom • Update formaat a h i n m 38 l e b k Algoritmiek c j d f g Analyse Union-Find met boomstructuur • Diepte van boom O(log n) • Dus: Find kost O(log n) tijd. • Union kost O(1) tijd, plus evt. tijd voor vinden van wortels (m.b.v. 1 of 2 finds). 39 Algoritmiek Diepte van boom bijhouden • In plaats van aantal knopen: houdt diepte van boom bij. • Noemen we rang. • Als rang(S1) < rang(S2) dan hang S1 onder S2 h • Als rang(S2) < rang(S1) n dan hang S2 onder S1 • Als rang(S1) = rang(S2) m l dan hang S2 onder S1 en tel 1 op bij de rang van S1 40 Algoritmiek a e b c i k j d f g Union by rank • Rang = diepte van boom • Als wortel rang r heeft, dan zijn er 2r knopen in zijn verzameling • O(log n) tijd voor find • O(1) tijd voor Union 41 Algoritmiek Padcompressie • Observatie: meerdere Finds op hetzelfde element herhalen veel werk. • De boom is niet binair – hoeft ook niet! • We kunnen elementen direct onder de wortel van de boom hangen: scheelt de volgende keer veel werk. 42 Algoritmiek Padcompressie d d c a b a 43 Algoritmiek b c Padcompressie - pseudocode Findwithpathcompression(x) r = x; while parent(r) r do r = parent(r); y = x; Find met padcompressie kost O( find zonder padcompressie ): while y r We gaan 2 keer het pad af do z = parent(y); parent(y) = r; y = z; return(r); 44 Algoritmiek Snelle en langzame functies • Ackermann; k 0; j 1: – A0(j) = j+1 j+1 keer – Ak(j) = Ak-1Ak-1…Ak-1(j) als k>0. • Inverse Ackermann – a(n) = min{ k>0 | Ak(1) > n} • Log-ster i keer – log*(n) = min {i | log log … log (n) < 1} • Inverse Ackermann in praktijk nooit meer dan 4; log-ster in praktijk nooit meer dan 5. 45 Algoritmiek Union-find met union by rank en padcompressie • Stelling. Als we n creates en m finds doen, dan kosten de finds samen Q(m a(m,n)) tijd. • We gaan bewijzen: • Stelling. Als we n creates en m finds doen, dan kosten de finds samen O(n+m log* n) tijd. 46 Algoritmiek Observaties over rangen • Op ieder pad omhoog in de boom stijgen de rangen. • De rang van een knoop is initieel 0, kan alleen maar stijgen gedurende de loop van het algoritme, en blijft gelijk nadat de knoop geen wortel meer is. • De waarde van de rang(parent(x)) daalt nooit. • Als x wortel van een boom is, dan is het formaat van de verzameling van x minstens 2rang(x). • Er zijn hooguit n / 2r knopen met rang r. • Iedere knoop heeft rang hooguit log n. 47 Algoritmiek Verdeel de rangen in blokken • Rang r zit in blok log* r. • Schrijf B(-1)=-1; B(0)=0; B(1) = 1. • B(j) = 2 2 2 j keer, j>1 • Blok j is {B(j – 1)+1, … , B(j)}. 48 Algoritmiek B(j) = 2B(j-1) Kosten van find Indelen in categorieën • 1 eenheid kosten per knoop op wortelpad. – Wortel: O(1) per find – Kind van wortel: O(1) per find – Blokkosten: Voor ieder blok: bovenste knoop op wortelpad met rank in blok. – Springkosten: Knopen met een rang anders dan de rang van de wortel, en niet bovenste knoop in blok – Padkosten: Alle andere knopen 49 Algoritmiek Blokkosten en springkosten • Er zijn O(log* n) blokken. Iedere find heeft hooguit 1 knoop per blok die blokkosten krijgt: – O(log* n) per find blokkosten. • Springkosten: – Nadat een knoop springkosten gekregen heeft, zal het daarna alleen nog maar wortel, kind van wortel, of blokkosten krijgen: • Nooit meer springkosten en padkosten • Want bovenste knoop in blok op elk wortelpad. • Dus totaal O(1) per knoop = O(n) totaal over alle finds 50 Algoritmiek Padkosten • Als knoop padkosten krijgt, dan – Zit zijn ouder met rang in zelfde blok. – Krijgt hij een nieuwe ouder met een kleinere rang. • Knoop in blok j kan B(j) – B(j – 1) – 2 keer padkosten krijgen: daarna zit zijn ouder zeker in een ander blok. (Blok j heeft B(j) – B(j – 1) elementen.) • Totale padkosten: som over alle blokken van aantal knopen met rang in dat blok * (B(j) – B(j – 1) –2). • Aantal knopen met rang in blok j: schrijf dit als N(j) 51 Algoritmiek N(j) 3n/2B(j) Gebruik dat er hooguit n/2r knopen met rang r zijn N(0) n / 20 + n / 21 = 3n / 2B(0) Als j>0: B( j ) n N ( j) r r B ( j 1) 1 2 n 2 B ( j 1)1 52 B ( j ) ( B ( j 1) 1) r 0 1 n 1 n n B ( j 1)1 r B ( j 1) r 2 2 2 B( j ) r 0 2 Algoritmiek Tellen van padkosten log*n 1 j 0 N ( j ) * B( j ) B( j 1) 2 j 0 log*n 1 3 n log* n 2 53 Algoritmiek 3n B( j ) 2 B( j ) Totaal • • • • • 54 Wortel: O(m) Kind van wortel: O(m) Blokkosten: O(m log* n) totaal Springkosten: O(n) Padkosten: O(m log* n) Algoritmiek Union find met padcompressie en union by rank • Een enkele find kan veel tijd (O(log n)) kosten. • Maar alle finds bij elkaar nauwelijks meer dan lineaire tijd! • Nuttig principe: – Extra werk maar niet veel om later tijd te besparen. 55 Algoritmiek Toepassing voor schedulingprobleem • n taken – Iedere taak kost evenveel (1) tijd – Taken hebben een deadline di – Taken hebben een opbrengst gi – Een taak brengt gi op dan en slechts dan als de taak uitgevoerd is op tijd di of eerder • Welke taken voeren we uit, en in welke volgorde, zodat – Taken uitgevoerd uiterlijk op deadline – Totale opbrengst zo groot mogelijk • Stel we hebben taken al gesorteerd op dalende opbrengst. 56 Algoritmiek Disjuncte verzamelingen • Neem een verzameling bestaande uit – Tijdstip dat nog niet gebruikt is – Alle latere gebruikte tijdstippen voor het volgende nog niet gebruikte tijdstip. • Bijhouden met union-find datastructuur; voor elke verzameling houden we het vrije tijdstip bij. 57 Algoritmiek Implementatie van `rechtsaanschuif’ algoritme • Zoeken van leeg tijdstip: find operatie • Plannen van taak op tijdstip t: – Union van verzameling die t bevat en de verzameling die t – 1 bevat. • Totale tijd: – O(n a(2n,n)) : bijna linear! 58 Algoritmiek Conclusie • Drie methoden voor analyse van geamortiseerde tijd van data structuur • Union-find datastructuur • Toepassing van UF komt ook bij algoritme voor minimum opspannende bomen 59 Algoritmiek