Algorithmen und Datenstrukturen Prof. Martin Lercher Institut für Informatik Heinrich-Heine-Universität Düsseldorf Teil 11 10 Suche in Graphen Designtechniken für Algorithmen Version vom Januar 2017 Version vom 13.12. Dezember 2016 FortsetzungVorlesungvom13.Januar SucheimTelefonbuch Exhaus'veSuche • GehevonA-ZalleEinträgedurch • O(N) BinäreSuche • SchlagedasTelefonbuchinderMiHeauf Sindwirzuweitodernochnichtweitgenug? GeheindieMiHederrelevantenHälLe … • O(log(N)) Interpola'ons-Suche • O(log(log(N))imDurchschniH Design-Techniken • VieleAlgorithmenbenutzenähnlicheIdeen/Konzepte • Hier:zunächsteinkurzerÜberblick • Ziel:lernezuerkennen,obeinneuesProblemdurcheineStandard-Technik gelöstwerdenkann Beispielproblem: WoinmeinerWohnungliegtdasklingelndeHandy? Exhaus]veSuche(BruteForce) Design-Idee: UntersuchenacheinanderjedemöglicheAlterna]ve. • Ignoriertevt.verfügbarezusätzlicheInforma]onen(z.B.dieRichtung,aus derdasKlingelnkommt) • EinfachstesVerfahren(Implemen]erung) • LangsamstesVerfahren(Komplexität) • VorhandeneLösungwirdgaran]ertgefunden • FürsehrkleineProblemerealis]sch Branch-and-Bound Abgekürzteexhaus]veSuche. Design-Idee: VerfolgeSuchpfadenursolange,bisklarist,dasssienichtzumErgebnis führen. Beispiel:wenndasKlingelnvonObenkommt,brichdieSucheimKellerab. GierigeAlgorithmen(Greedyalgorithms) Design-Idee: BeiderWahlzwischenAlterna]ven:wählediejenige,die(nachkurzer, einfacherAbwägung)alsdievielversprechendsteerscheint. Beispiel:laufedirektinRichtungdesKlingeltons. Aber:waswenndasHandyimGartenliegtundichhöredasKlingeln durch’sFenster? • SehrschnellesVerfahren • Problem:der‘vielversprechendste’Pfadkannlangfris]gderfalschesein (lokaleOp]ma) DynamischeProgrammierung Design-Idee: Wenniden]scheTeilproblemeimmerwiedergelöstwerdenmüssen: recycling(miteinerTabelle). Beispiel:Steinchen-Spiel. • ZweiStapelmitje10Steinen. • DieSpielernehmenabwechselndentwedereinenSteinoderzweiSteine(je einemvonjedemStapel). • DerSpielerderdenletztenSteinnimmtgewinnt. Fragen: • GibteseinesichereGewinnstrategie? • WelcherSpielergewinnt? DynamischeProgrammierung 0 0 1 2 3 4 5 6 7 8 9 1 W W W 2 3 4 5 6 7 8 9 10 Divide&Conquer(Teile&Herrsche) Design-Idee: SpalteursprünglichesProbleminTeilprobleme,lösejedesfürsich,setze ErgebnissezuGesamtlösungzusammen. Beispiele: • Quicksort • Mergesort MachineLearning(MaschinellesLernen) Design-Idee: BildeAlgorithmusaufgrundvonMusternausBeispieldatensätzen. Beispiel: FühreBuchüberdieAblagestelledesHandys.Suchezuerstamhäufigsten Platz(z.B.80%Küche),dannamzweithäufigstenPlatz(12%Wohnzimmer), etc. Wannkannman welchesDesignverwenden? Exhaus]veSuche -immer,istaberineffizient- Wannkannman welchesDesignverwenden? GierigeAlgorithmen Greedy-ChoiceEigenschaL Die‘Greedy-Choice’EigenschaLbesagt,dasslokalop]maleEntscheidungen (=greedychoices)zueinemglobalenOp]mumführen. IndiesemFallführtdergierigeAlgorithmuszueinemexaktenErgebnis. Wannkannman welchesDesignverwenden? Divide&Conquer Divide-and-Conquer Einedivideandconquer(TeileundBeherrsche)Methodeeignetsichfür Probleme,dierekursivinTeilproblemezerlegtwerdenkönnen(bisdie Teilproblemesoeinfachsind,dasssiedirektgelöstwerdenkönnen). Divide-and-Conquerfunk]oniertdanngut,wenndieAnzahlderTeilprobleme injedemSchriHkleinbleibtundwenndieZusammensetzungderTeillösungen einfachist. Wannkannman welchesDesignverwenden? DynamischeProgrammierung Op]maleSubstruktur EinProblemhateineop]maleSubstrukturgenaudann,wenneineop]male LösungdesProblemsop]maleLösungenvonTeilproblemenenthält. WenneinProblemop]maleSubstrukturhat,könnteessichfürLösungdurch dynamischeProgrammierungeignen. ÜberlappendeTeilprobleme UmfürdynamischeProgrammierunggeeignetzusein,mussderRaumder Teilproblemekleinsein:einrekursiverAlgorithmuswürdedasselbe Teilproblemimmerwiederlösen. DieGesamtzahlderTeilproblemeisttypischerweisepolynomialinder Eingabegröße. Hinweis WenneinrekursiverAlgorithmusimmerneue(bisherungelöste)Teilprobleme erzeugt,dannistvermutlicheineDivide-and-ConquerStrategiebesser geeignet. Vorlesungvom17.Januar VERSCHIEDENEALGORITHMENFÜR DASSELBEPROBLEM 21 Ak]enproblem Gesucht:BesterKauf-undVerkaufstagfüreineAk]e Gegeben:KursänderungenproTag Problem:FindeeinIndex-PaarKV:=(i,j)ineinemArraya[1..n]vonganzen Zahlen,fürdasf(i,j)=ai+ai+1+…+ajmaximalist. AlsRechenschriHezählenarithme]scheOpera]onenundVergleiche. 22 BruteForce(Naiv,exhaus]veSuche) Eswirdf(i,j)füralleiundjberechnetunddarausdermaximaleWert bes]mmt.ZurBerechnungvonf(i,j)benö]genwirgenauj-iAddi]onen.Wir beginnenmitOffensichtlichgenügenzurBerechungvongenau Addi]onen.DerAlgorithmusstartetmitKV=f(1,1)undaktualisiertKVwenn nö]g. Laufzeit: T (n) ∈ Θ(n 3 ) 23 DynamischeProgrammierung(Naïve++) DernaiveAnsatzberechnetvieleTeilsummenmehrfach,z.B.a1+a2für f(1,2),f(1,3),…f(1,n),alson-1mal.DurchWiederverwendungder TeilsummensspartmanAufwand: f(i,j+1)=f(i,j)+aj+1 DamitkönnenalleWertef(i,j)fürfestesiingenau(n-i)SchriHenbes]mmt werden. Laufzeit: T (n) ∈ Θ(n 2 ) 24 Divide&Conquer M M+1 DiegrößteTeilfolgeliegtentweder: • imlinkenTeil; • imrechtenTeil; • oderteilslinks,teilsrechts Alslinkes(rechtes)RandmaximumbezeichnenwirdieSummederTeilfolge al,...,aM(aM+1,...,ar),fürdiedieSummemaximalwirdunterallen Teilfolgen,diemitl∈[1,M]beginnen(mitr∈[M+1,n]enden). 25 AufwandfürdieBes]mmungderRandmaxima • links:M−1Addi]onenundebensovieleVergleiche • rechts:(n−M−1)Addi]onenundebensovieleVergleiche • Summe:2·n−2SchriHe DieRandmaximaseienrlundrr. FallsdiemaximaleTeilfolgedenRandaMumfaßt,soistal+…+areine maximaleTeilfolgemitTeilsummerl+rr. 26 Divide&Conquer Divide LöseTeilproblemea1,...,aMundaM+1,...,anrekursiv. DieMaximaseienslundsr Merge DiemaximaleTeilsummeergibtsichnunals max{sl,sr,rl+sr} 27 sieheVorlesungTeil2: Master-Theorem (Hauptsatz der Laufzeitfunktionen) Satz Seien a, b, k 2 N, mit a 1, b 2 und k 0. Sei die Laufzeit eines Algorithmus durch folgende Rekurrenz-Relation beschrieben: ⇣n⌘ T (n) = aT + f (n) b mit f (n) 2 ⇥(nk ). Dann gilt: T (n) 2 ⇥(nk ) wenn T (n) 2 ⇥(nk log (n)) wenn T (n) 2 ⇥(nlogb (a) ) wenn a < bk a = bk a > bk Gilt genauso auch für O (mit T (n) ...) und ⌦ (mit T (n) ...). Beweis. ...mit Analysis, siehe z.B. Jonsonbaugh and Schaefer S. 60-65... 30 / 94 Aufwand Sein=2k,undwirteilenjeweilsinderMiHe,M=n/2. T (n) = 2T (n / 2) + 2n ⇒ T (n) ∈Θ(n log(n)) mitdemMaster-Theorem 29 Scanline bismax scanmax WirlaufenmitderScanlinievonlinksnachrechtsundmerkenunsdabeizweiZahlen: bisMax=diegrößtebishergeseheneTeilfolge ScanMax=daslinkeRandmaximumderScanlininie while(nächstesElementajexis]ert){ If(ScanMax+aj>0) ScanMax=ScanMax+aj else ScanMax=0 bisMax=max(bisMax,ScanMax) } Laufzeit: T (n) ∈ Θ(n) 30 ZusammenfassungdesAk]enproblems • • • • BruteForce DynamischeProgr. Divide&Conquer Scanline RechenschriSe: 31 Divide&Conquer DieDivide&ConquerTechnik aproblemofsizen subproblem1 ofsizen/2 subproblem2 ofsizen/2 asolu'onto subproblem1 asolu'onto subproblem2 asolu'onto theoriginalproblem Divide&Conquer DiewohlbekanntesteDesign-Strategie 1. TeileeineInstanzdesProblemsin2odermehrkleinereInstanzen 2. LösediekleinerenInstanzenrekursiv(oderdirekt,wennsiekleingenug sind) 3. ErhalteeineLösungfürdieursprünglicheProbleminstanzdurcheine Kombina]onderLösungenderkleinerenInstanzen • OLkönnenDivide&ConquerAlgorithmenrekursivdefiniertwerden (nichtimmernö]g!) • Eintop-downAnsatzzurProblemlösung EinKachel-Problem EinTrominoisteinObjektausdrei1x1Quadraten: EinnxnBreH,demein1x1Feldfehlt(d.h.miteinerLücke)isteindefizientes BreH. GegebeneindefizientesnxnBreHmitn=2k,bedeckedasBreH–ohne Überlappungen–mitTrominos. 2x2: 4x4: EinKachel-Problem LösungmitDivide&Conquer: • TeiledasnxnSpielbreHin4BreHermitn/2xn/2Feldern. • Einesder4TeilbreHerenthältdieLücke;LegeeinenTrominoindieMiHe, sodassermitdenanderen3TeilbreHernüberlappt. • BetrachtedieFelderdiesesTrominosals“Lücken”–dannhatjedes TeilbreHgenaueineLücke. • Lösealleviern/2xn/2Teilproblemerekursiv • Fürn=2istdieLösungtrivial(einTromino)–Rekursionsende. KachelneinesdefizientenBreHsmitTrominos Eingabe: n, a power of 2 (the board size); L, the location of the missing square tile(n,L) { if (n == 2) { // the board is a tromino T tile with T // in the right orientation vs. L return } divide the board into four n/2 × n/2 subboards place 1 tromino centrally, making all subboards deficient let m1,m2,m3,m4 be the locations of the missing squares tile(n/2,m1) tile(n/2,m2) tile(n/2,m3) tile(n/2,m4) } Laufzeit? Analyse • Annahme:‘]lewithT’undBerechnungvonmiinO(1) • DannistdieLaufzeitpropor]onalzurAnzahldergelegtenTrominos: n2 − 1 = Θ(n 2 ) 3 Realworld:DesignvonComputerchips Mergesort • TeileArrayin2HälLen – TrivialfürLänge1 – SonstRekursion • Merge(Verschmelze)diesor]ertenHälLen,indemPosi]onszeigerbeide durchläuLundjeweilsdiekleinereZahlinHilfsarraykopiert. AlgorithmusMergesort(F): • FallsFnureinElementenhält,gibFzurück • Divide:TeileFinzweigleichgroßeTeilfolgenF1undF2 • Conquer:Mergesort(F1),Mergesort(F2) • Merge:VerschmelzeF1undF2 Closest-PairProblem FindediezweiPunktemitgeringstemeuklidischemAbstand. Closest-PairProblem:DivideandConquer • Bruteforce:vergleichejedenPunktmitjedemanderen • BeinPunkten: n−1 ∑k = k=1 (n − 1)⋅ n ∈O(n 2 ) 2 • Divide&Conquer:O(nlogn) Closest-PairProblem:DivideandConquer • Sor]erediePunktein1Dimension(x-Achse):O(nlogn)mitMergesort • Divide: – TeileinzweiHälLenundbes]mmeclosestpairinjederHälLe. – bei≤3Punkten:bes]mmeclosestpairdirekt • Conquer(ähnlichwieScanLine): – derkleinsteAbstandliegtlinks(δl)oderrechts(δr) odererüberstreichtdieGrenze:dannliegenbeidePunktejeweilsim Streifenmitmax.Abstandδ=min(δl,δr) – WennwirallePunkteimStreifenuntersuchen,sinddasimworstcase n,alsoΩ(n2)Vergleiche! – Aber:wirmüssennurPunktevergleichen,diemaximaldenAbstandδ aufdery-Achsehaben… – …unddassindmaximal8Punkte,denn:wärenineinemAchteldeszu durchsuchendenRechteckszweiPunkte,wärederenAbstandmaximal (Diagonale!): ⎛⎛ δ ⎞2 ⎛ δ ⎞2⎞ δ + = <δ ⎜ ⎜⎝ 2 ⎟⎠ ⎜⎝ 2 ⎟⎠ ⎟ 2 ⎝ ⎠ Closest-PairProblem:DivideandConquer – Aber:wirmüssennurPunktevergleichen,diemaximaldenAbstandδ aufdery-Achsehaben… – …unddassindmaximal8Punkte,denn:wärenineinemAchteldeszu durchsuchendenRechteckszweiPunkte,wärederenAbstandmaximal (Diagonale!): ⎛⎛ δ ⎞2 ⎛ δ ⎞2⎞ δ ⎜ ⎜⎝ 2 ⎟⎠ + ⎜⎝ 2 ⎟⎠ ⎟ = 2 < δ ⎝ ⎠ δ δ δ 43 Input Parameters: List of points p=p[1], …, p[n] closest_pair(p) { mergesort(p,1,n) // sort p by x-coordinate p[i].x return rec_cl_pair(p,1,n) } // rec_cl_pair assumes that input is sorted by x-coordinate. // At termination, the input is sorted by y-coordinate. rec_cl_pair(p,i,j) { if (j - i < 3) { // simple case, no further recursion: mergesort(p,i,j) // sort by y-coordinate // find the distance delta between a closest pair delta = dist(p[i],p[i + 1]) // Euclidean distance if (j - i == 1) // two points return delta // three points if (dist(p[i + 1],p[i + 2]) < delta) delta = dist(p[i + 1],p[i + 2]) if (dist(p[i],p[i + 2]) < delta) delta = dist(p[i],p[i + 2]) return delta } ... ... } // we only get here if n>3; recursion: k = (i + j)/ 2 // division point l = p[k].x // x coordinate of p[k] deltaL = rec_cl_pair(p,i,k) deltaR = rec_cl_pair(p,k + 1,j) delta = min(deltaL,deltaR) // p[i], ... , p[k] is now sorted by y-coordinate, and // p[k + 1], ... , p[j] is now sorted by y-coordinate. merge(p,i,k,j) // the standard merge from mergesort // p[i], ... , p[j] is now sorted by y-coordinate. // store points in the vertical strip in v. t = 0 for k = i to j if (p[k].x > l - delta && p[k].x < l + delta) { t = t + 1 v[t] = p[k] } // look for closer pairs in the strip by comparing // each point in the strip to the next 7 points. for k = 1 to t - 1 for s = k + 1 to min(t,k + 7) delta = min(delta,dist(v[k],v[s])) return delta Analyse Worst-caseLaufzeit: • Sor]erennachx-Koordinate:Θ(nlogn)mitMergesort • Sor]erennachy-Koordinate:Θ(nlogn)mitMergesort • LaufzeitderRekursionen: Tn ≤ T⎢⎣n/2 ⎥⎦ + T⎢⎣(n+1)/2 ⎥⎦ + cn fürn>3 =>O(nlogn) Mul]plika]onganzerZahlen Z=X*Y=O(1)imEinheitskostenmaß; BeigroßenZahlenlogarithmischesKostenmaß(digits)besser! SeidieLänge(digits)vonXundYjeweilsetwan. StandardmethodeausderSchule:Θ(n2) TeileX,YjeweilsinHälLenXLXR,YLYR Bsp.:X=123|456,Y=789|123 Z=XY=XLYL106+(XLYR+XRYL)103+XRYR Tn = 4Tn/2 + cn ⇒ Θ(n 2 ) Mul]plika]onganzerZahlen TeileX,YjeweilsinHälLenXLXR,YLYR Bsp.:X=123|456,Y=789|123 Z=XLYL106+(XLYR+XRYL)103+XRYR WirbraucheneineMul]plika]onweniger! XLYR+XRYL=(XL-XR)(YR-YL)+XLYL+XRYR dieletztenbeidenTermewerdenohnehinberechnet! Tn = 3Tn/2 + cn ⇒ Θ(n log2 3 ) ≈ Θ(n1.59 ) Strassen’s(1969)Matrixprodukt SeienAundBzweinxnMatrizenmitn=2kundseiC=AB. TeileAundBinje4n/2xn/2–Matrizen. c00c01a00a01b00b01 = c10c11a10a11b10b11 a00b00+a01b10 a00b01+a01b11 = a10b00+a11b10 a10b01+a11b11 4(n/2)2Opera]onenzumVerknüpfenderTeillösungen,also Tn = 8Tn/2 + cn 2 ⇒ O(n 3 ) Strassen’s(1969)Matrixprodukt c00c01a00a01b00b01 = c10c11a10a11b10b11 m1+m4-m5+m7 m3+m5 = m2+m4 m1+m3-m2+m6 m1=(a00+a11)*(b00+b11) m2=(a10+a11)*b00 7 Multiplikationen! m3=a00*(b01-b11) 18 Additionen (Θ(n2)) m4=a11*(b10-b00) m5=(a00+a01)*b11 Tn = 7Tn/2 + cn 2 m6=(a10-a00)*(b00+b01) log 7 ⇒ Θ(n m7=(a01-a11)*(b10+b11) ) Vorlesungvom20.Januar GierigeAlgorithmen -GreedyAlgorithms- Op]mierungsproblem:GierigesGeldwechseln Problem: Gebe Wechselgeld für den Betrag A mit möglichst wenig Scheinen +Münzen aus. Scheine und Münzen haben die in denom absteigend sortiert gespeicherten Beträge. Input Parameters: denom,A greedy_coin_change(denom,A) { i = 1 while (A > 0) { c = A/denom[i] // ganzzahlige Division print(“use ”+c+“ coins of denomination ”+denom[i]) A = A - c * denom[i] i = i + 1 } } Op]mierungsproblem:GierigesGeldwechseln Theorem: Greedy Coin Change (GCC) ist optimal für denom=(10,5,1), d.h. GCC gibt eine minimale Anzahl von Münzen aus. Beweis: vollständige Induktion über A. Induktionsanfang: für A<5 können nur 1-er ausgeben werden. Induktionsschritt: 1. Fall: 5≤A<10. Dann muss jede optimale Lösung einen 5-er beinhalten; ansonsten könnten 5 1-er durch einen 5-er ersetzt werden und die Lösung wäre nicht optimal. Die von GCC ausgegebene Lösung für A‘:=A-5 ist nach Induktionsanfang optimal, damit auch die Lösung für A. 2. Fall: 10≤A. Dann muss jede optimale Lösung einen 10-er beinhalten; sonst könnten 2 5-er (oder 1 5-er und 5 1-er oder 10 1-er) durch einen 10-er ersetzt werden und die Lösung wäre nicht optimal. Die von GCC ausgegebene Lösung für A‘:=A-10 ist nach Induktionsvoraussetzung optimal, damit auch die Lösung für A. qed Op]mierungsproblem:GierigesGeldwechseln Greedy Coin Changing ist auch optimal für EUR und $ (Bw. analog). Greedy Coin Change ist nicht optimal für denom=(10,7,5,1). (Nachweis durch Test für A=14). Eine Verbesserung durch dynamische Programmierung ist für jede Stückelung der Scheine und Münzen denom optimal. MinimumSpanningTree(MST)Problem Problem: GegebenseieinzusammenhängendergewichteterGraphG(d.h.jede KantehateinGewichtausdenreellenZahlen>0). FindeeinenzusammenhängendenTeilgraphen,deralleKnotenvonG enthält,undfürdendieSummederKantengewichteminimalist. Bemerkung:DieserTeilgraphisteinBaum. A 3 D 4 5 7 3 B 5 6 E 4 3 7 H C 6 F 6 5 4 5 I 5 G 4 J GierigerAlgorithmuszurMST-Suche,Version1 A1 1. BeginneaneinembeliebigenKnoten. 2. WählediekürzesteVerbindungzueinemunbesuchtenNachbarn. 3. Fahremit(2.)fortbisalleKnotenbesuchtsind. LiefertA1eineop]maleLösung(einenMST)? Warumnicht? Weilwirlokalop]maleEntscheidungen 4 5 A B C treffen. 3 D 5 7 3 6 E 4 3 7 H 6 F 6 5 4 5 I 5 G 4 J GierigerAlgorithmuszurMST-Suche,Version2: KruskalsAlgorithmus KruskalsAlgorithmus 1. WähleeineKantemitminimalemGewicht,diekeinenZykluserzeugt. 2. Fahremit(1.)fortbisalleKnotenbesuchtsindundwireinen zusammenhängendenTeilgraphenerhalten. LiefertKruskalsAlgorithmuseineop]maleLösung(einenMST)? Warum? 4 5 A B C Weilwirglobalop]maleEntscheidungen 3 6 4 5 6 treffen. D 7 3 E 3 7 H F 6 5 4 5 I 5 G 4 J Implemen]erungvonKruskalsAlgorithmus ZubeachtendePunkte: • BezeichnungderKnotenmitihremIndex1…n • Repräsenta]ondesGraphensalseineListevonKantenmitGewichten. Bsp.:(1,2,1),(1,4,3),… • WirwählenjeweilsKantenmitminimalemGewicht;dazusor]erenwirdie Kantenzunächst. • EineKantezwischenKnoteni,jerzeugteinenZyklus ⇔ i,jsindbereitsverbunden ⇔ i,jgehörenzurgleichenZusammenhangskomponente(“Komponente”) Funk]onenfürüberschneidungsfreieMengen(Disjointsets): • Makeset(i) ErzeugeeineMengemitdemeinzigenElementi • findset(i) FindedieMenge,dieienthält • union(i,j) VereinigedieMengenmitdenElementeniundj A 3 D 3 3 7 5 B 4 E 7 H KruskalsAlgorithmus Eingabe: edgelist (die Liste der Kanten mit Komponenten v, w, g) und n. Die Ausgabe gibt die Kanten in einem MST. sort sortiert edgelist nach austeigendem Gewicht g. kruskal(edgelist,n) { sort(edgelist) // edgelist[1] ist jetzt die kürzeste Kante for i = 1 to n makeset(i) // jeder Knoten eine eigene Komponente count = 0 // Anzahl Knoten im MST bisher i = 1 // die erste zu untersuchende Kante while (count < n - 1) { if (findset(edgelist[i].v) != findset(edgelist[i].w)) { println(edgelist[i].v + “ ” + edgelist[i].w) count = count + 1 union(edgelist[i].v,edgelist[i].w) //merge components } i = i + 1 } } KorrektheitvonKruskalsAlgorithmus Theorem: SeiGeinverbundenergewichteterGraph.SeiG’einSubgrapheinesMSTT vonG.SeiCeineZusammenhangskomponentevonG’.SeiSdieMengealler KantenmiteinemKnoteninCunddemanderenKnotennichtinC. WennwireineKantemitminimalemGewichtausSzuG’hinzufügen,dannist derresul]erendeSubgraphebenfallsineinemMSTT’enthalten. Korrolar HierausfolgtdieKorrektheit(Op]malität)vonKruskalsAlgorithmus. Bw.(Korrolar):pervollständigeInduk]on. WirverfolgenKruskalsAlg.undzeigenmitHilfedesTheoremsjeweils,dasswir immernochineinemMSTenthaltensind.SobaldalleKnoteninCliegen habenwirdenvollständigenMST.qed KorrektheitvonKruskalsAlgorithmus Beweis(Theorem):Sei(v,w)eineKantemitminimalemGewicht,mitvinCund wnichtinC,undCenthalteninG’.SeiTderMST,derG’enthält. Falls(v,w)inTenthaltenist,sindwirfer]g. Annahme:(v,w)istnichtinTenthalten.Füge(v,w)zuThinzu.Dieserzeugt einenZyklusS.Seiw’derersteKnoteninSaufdemalterna]venWegvonv nachw,dernichtinCliegt.Seiv’dervorhergehendeKnoten.EnŽernedie Kante(v’,w’);diesergibtdenneuenSpanningTreeT’. (v,w)isteineKantemitminimalemGewichtmiteinemKoteninCunddem anderennichtinC.Dahergilt weight(v’,w’)≥weight(v,w) => weight(T’)≤weight(T) DaTeinMSTist,mussauchT’einMSTsein.qed Vorlesungvom24.Januar ParentPointer-Implemen]erungvonMengen Alle Elemente sind in einer Liste gespeichert. Wir ordnen jedem Element (Knoten) einen Index zu. Wir können direkt vom Index auf den Index des Vaters zugreifen. Eine Liste von 1-knotigen Bäumen (1-elementigen Mengen): Index 1 2 3 4 5 6 7 8 9 10 Parent 1 2 3 4 5 6 7 8 9 10 Knoten A B C D E F G H I Index 1 2 3 4 5 6 7 8 9 10 Parent 6 1 1 6 4 6 6 3 6 10 Knoten A B C D E F G H I Eine Liste mit 2 Bäumen (2 Mengen) J J Makeset&Findset,Version1 This algorithm represents the set {i} as a one-node tree. Input Parameter: i makeset1(i) { parent[i] = i } This algorithm returns the root of the tree to which i belongs. Input Parameter: i findset1(i) { while (i != parent[i]) i = parent[i] return i } Mergetrees&Union,Version1 This algorithm receives as input the roots of two distinct trees and combines them by making one root a child of the other root. Input Parameters: i,j mergetrees1(i,j) { parent[i] = j } This algorithm receives as input two arbitrary values i and j and constructs the tree that represents the union of the sets to which i and j belong. The algorithm assumes that i and j belong to different sets. Input Parameters: i,j union1(i,j) { mergetrees1(findset1(i), findset1(j)) } Makeset&Findset,Version2:height This algorithm represents the set {i} as a one-node tree and initializes its height to 0. Input Parameter: i makeset2(i) { parent[i] = i height[i] = 0 } This algorithm returns the root of the tree to which i belongs. Input Parameter: i findset2(i) { while (i != parent[i]) i = parent[i] return i } Mergetrees&Union,Version2:height This algorithm combines trees by making the root of the tree of smaller height a child of the other root. Input Parameters: i,j mergetrees2(i,j) { if (height[i] < height[j]) parent[i] = j else if (height[i] > height[j]) parent[j] = i else { parent[i] = j height[j] = height[j] + 1 } } This algorithm constructs the union of the sets to which i and j belong. The algorithm assumes that i and j belong to different sets. Input Parameters: i,j union2(i,j) { mergetrees2(findset2(i), findset2(j)) } LaufzeitderMengenopera]onen Theorem:DieHöhederdurchdie“Version2”-Mengenopera]onen entstehendenBäumefürMengenmitkElementenisthöchstenslogk. Beweis:durchvollständigeInduk]on. Induk]onsanfang:EinBaummit1Element(1Knoten)hatHöhe0=log(1). Induk]onsschriH:Seik>1unddieBehauptungfürallep<kbereitsgezeigt. SeiTeindurchdieMengenopera]onenerzeugterBaummitkKnoten.Wegen k>1istTdurchdieVereinigungvonzweiTeilbäumenentstanden(T1mitHöhe h1undk1<kKnoten,sowieT2mitHöheh2undk2<kKnoten). NachInduk]onsvoraussetzungisth1≤logk1undh2≤logk2. Fall1:h1≠h2.Danngilth=max{h1,h2}≤max{logk1,logk2}<log(k). Fall2:h1=h2.SeiohneBeschränkungderAllgemeinheit(oBdA)k1≥k2. Wegenk=k1+k2folgtk2≤½k. h=1+h2≤1+logk2≤1+log(½k)=logk.qed 69 KruskalsAlgorithmus:Laufzeit kruskal(edgelist,n) { sort(edgelist) // edgelist[1] ist jetzt die kürzeste Kante for i = 1 to n makeset(i) // jeder Knoten eine eigene Komponente count = 0 // Anzahl Knoten im MST bisher i = 1 // die erste zu untersuchende Kante while (count < n - 1) { if (findset(edgelist[i].v) != findset(edgelist[i].w)) { println(edgelist[i].v + “ ” + edgelist[i].w) count = count + 1 union(edgelist[i].v,edgelist[i].w) //merge components } i = i + 1 } } FürnKnoten&mKanten(mitn<m): • Θ(mlogm)fürsort(edgelist) • makesetwirdn-maldurchlaufen,jeweilsO(1) • DieSchleifewirdmaximalmmaldurchlaufen; alleMengenopera]onensindausO(logn) DieGesamtlaufzeitistdaherausO(mlogm). PrimsAlgorithmus • FindetebenfallsMSTineinemverbundenen,gewichtetenGraphen. • TeillösungensindbereitsBäume(andersalsbeiKruskalsAlgorithmus!). Algorithmus: 1. BeginnemiteinemStartknoten(1-kno]gerBaum). 2. FügeeineKantemitminimimalemGewichthinzu,dieeineninzidenten Knotenimgegenwär]genBaumundeinenKnotenausserhalbhat. 3. Fahremit(2.)fort,bisalleKnotenverbundensind(alson-1mal). Implemen]erung: • BuchführungüberKandidaten-Kantenwirdeinfacher,wennwirproKnoten ausserhalbdesBaumsnureineminimaleKantezumBaumauflisten. • ArrayhmitKnotenvausserhalbdesBaumsunddemGewichteiner minimalenKante(v,w)vonvzumBaum. • ArrayparentmitdenzugehörigenKanten:parent[v]=w. • hundparentmüsseninjederItera]onangepasstwerden.Hierzumüssenalle KantenvomzuletztzumBaumhinzugefügtenKnotenvbetrachtetwerden. PrimsAlgorithmus histabstrakterDatentyp(ADT)mitOpera]onen: • h.init(key,n)-ini]alisierthmitdenWertendesArrayskey[1…n] • h.del()-enŽerntdenEintragmitminimalemGewichtinhundgibtden entsprechendenKnotenaus(–>HinzufügenvonKnotenzumMST). • h.isin(w)-gibtTRUEfallswinh,sonstFALSE (–>welcheKnotensindnochnichtimMST). • h.keyval(w)-gibtdaszuwgespeicherteGewichtzurück. • h.decrease(w,wgt)-ändertdasGewichtzuwaufwgt(–>wenneinneu hinzugefügterKnotenzueinerverbessertenErreichbarkeitvonwführt). EffizienteImplemen]erungz.B. mitHilfeeinesbinärenMin-Heaps undeinemHashfürisinundkeyval: Opera'on Worst-case 'me Init Θ(n) del Θ(logn) isin Θ(1) keyval Θ(1) decrease Θ(logn) PrimsAlgorithmus Dieser Algorithmus findet einen MST in einem zusammenhängenden gewichteten Graphen mit n Knoten. Der Graph wird durch Adjazenzlisten repräsentiert. adj[i] verweist auf den ersten Eintrag in einer verketteten Liste von Knoten, die alle Knoten enthält, die über eine Kante direkt mit Knoten i verbunden sind. Jeder Eintrag hat folgende Bestandteile: • ver, den mit i inzidenten Knoten; • weight, das Gewicht der Kante (i,ver); • next, ein Pointer auf den nächsten Knoten in der verketteten Liste (oder null für den letzten Eintrag). Der Startknoten ist start. Im MST gilt: der Vater von Knoten i ≠ start ist parent[i], und parent[start] = 0. ∞ bezeichnet die größte darstellbare ganze Zahl. Input Parameters: adj,start Output Parameters: parent prim(adj,start,parent) { n = adj.last // index of last entry = number of nodes for i = 1 to n key[i] = ∞ // key is a local array key[start] = 0 parent[start] = 0 h.init(key,n) // initialize h to the values in key for i = 1 to n { v = h.del() // delete & return smallest weight ref = adj[v] // nodes linked to v while (ref != null) { w = ref.ver if (h.isin(w) AND (ref.weight < h.keyval(w))) { parent[w] = v // update h and parent h.decrease(w,ref.weight) } Laufzeit(nKnoten&mKanten): ref = ref.next Θ(nlogn)fürdel } Θ(m)fürwhileloop } O(mlogn)fürdecrease } =>O(mlogn)insgesamt KorrektheitvonPrim’sAlgorithmus Theorem:Prim’sAlgorithmusistkorrekt,d.h.erfindeteinenMST. Beweis:durchVollständigeInduk]on.Wirzeigen,dassinjederItera]onvon Prim’sAlgorithumsdergegenwär]geBaumTineinemMSTenthaltenist. Induk]onsanfang:TrivialfürTmitnurdemStartknoten(ohneKanten). Induk]onsschriH: Induk]onsvoraussetzung:SeiG’derbisherigeBaumT’,erweitertumalle KnotennichtinT’. DannistT’eineKomponentevonG’,undT’istineinemMSTenthalten. DienächstevonPrimsAlg.gewählteKante(v,w),dieT’zuTerweitert,erfüllt danndieVoraussetzungendesfürdenBeweisderKorrektheitvonKruskal’s AlgorithmusbewiesenenTheorems.Darausfolgtsofort,dassauchTin einemMSTenthaltenist. AmEndevonPrim’sAlgorithmussindalleKnoteninTenthalten,damitistTein MST.qed Dijkstra’sAlgorithmus Dieser Algorithmus findet die kürzesten Wege von einem ausgewählten Knoten zu allen anderen Knoten in einem zusammenhängenden, gewichteten Graph mit n Knoten. Auf einem kürzesten Weg ist der Vorgänger von Knoten i predecessor[i], mit predecessor[start]=0. Wie bei Prim‘s Algorithmus: • Der Graph wird durch Adjazenz-Listen repräsentiert. • Der Wert ∞ ist die größte verfügbare ganze Zahl. • Der abstrakte Datentyp h verfügt über die selben Operationen. Input Parameters: adj,start Output Parameters: parent dijkstra(adj,start,parent) { n = adj.last // number of vertices for i = 1 to n key[i] = ∞ // key is a local array key[start] = 0 predecessor[start] = 0 h.init(key,n) // initialize the container h to key values for i = 1 to n { v = h.del() // delete & return smallest weight min_cost = h.keyval(v) // associated weight ref = adj[v] // list of nodes adjacent to v while (ref != null) { // update predecessor & h w = ref.ver // the linked node if (h.isin(w) AND (min_cost + ref.weight < h.keyval(w))) { predecessor[w] = v h.decrease(w, min_cost + ref.weight) } // end if ref = ref.next } // end while Laufzeit(nKnoten&mKanten): } // end for O(mlogn)insgesamt } (wiePrimsAlg.) KorrektheitvonDijkstrasAlgorithmus Theorem:DijkstrasAlgorithmusistkorrekt,d.h.erfindetdenkürzestenPfad voneinemKnotenzuallenanderenKnoten. BeweisperVollständigeInduk]on:injederItera]on,indervaushenŽernt wird,istkey(v)dieLängeeineskürzestenPfadesvonstartzuv. Induk]onsanfang:trivial,daderersteKnotenstartist. Induk]onsschriH:vwurdegeradeenŽernt;fürallevorherenŽerntenKnoteni giltkey(i)istLängeeineskürzestenPfades(Induk]onsvoraussetzung). Wirzeigenzunächst:gibteseinenkürzestenWegvonstartzueinemKnotenw derLänge<key(v),dannwurdewvorvenŽernt.Annahme,diesgiltnicht fürw.Dannseiw’derjenigeKnotenaufeinemkürzestenPfadPvonstartzu w,deramnächstenanstartliegtundnochinhist(imZweifelw=w’).Dann istseinVorgängerw’’nichtinh,undesgilt: key(w)≤key(w’)+weight(w’’,w’)≤lengthofP<key(v).Dannwarvnicht derKnoteninhmitdemkleinstenkey. InsbesonderekanneskeinenkürzerenWegvonstartzuvgeben(sonstwärev schonvorheraushenŽerntworden).qed HuffmanCodes File-Komprimierungdurchneue(non-ASCII)KodierungdesAlphabets. Standard-Kodierung(àlaASCII):CZeichenbenö]genlog2CBits. (7Bitsfür128Zeichen+1BitfürParity) Praxis:Dateienhabenz.B.häufigvieleZiffern,aberwenige„x“und„y“ WählekürzereRepräsenta]onfürhäufigereZeichen! (Bsp.) SolangeeinCodefüreinZeichennichtPräfixeinesanderenist,kannderCode verkürztwerden->Präfix-code(mitunterschiedlichenBit-ZahlenfürZeichen) DiesentsprichteinemvollständigenKodierungsbaum(jederinnereKnotenhat zweiSöhne). HuffmanCodes Huffman‘sAlgorithmus: WirsindFörstermiteinemWaldvonBäumen,dersichentwickelt.JederBaum hateinGewicht:=dieSummederHäufigkeitenseinerBläHer. Start: CBäumeausjeeinemBlaH. Wiederhole(C-1mal): Wähledie2BäumemitkleinstemGewicht(Tiessindegal)undverbinde siezueinemneuenBaum(durchHinzufügeneinergemeinsamenWurzel). (Bsp.) Huffman’sAlgorithm This algorithm constructs an optimal Huffman coding tree. The input is an array a of n ≥ 2 nodes. Each node has an integer member character to identify a particular character, another integer member key to identify that character’s frequency, and left and right members. After the Huffman coding tree is constructed, a left member of a node references its left child, and a right member of a node references its right child or, if the node is a terminal vertex, its left and right members are null. The algorithm returns a reference to the root of the Huffman coding tree. The operator, new, is used to obtain a new node. h is an ADT with the following properties. If a is an array, the expression h.init(a) initializes the container h to the data in a. h.del() deletes the node in h with the smallest key and returns the node. h.insert(ref ) inserts the node referenced by ref into h. Huffman’sAlgorithm Input Parameters: a Output Parameters: None huffman(a) { h.init(a) for i = 1 to a.last - 1 { // from 1 to n-1 ref = new node // new root for two smallest trees ref.left = h.del() // smallest tree ref.right = h.del() // 2nd smallest tree ref.key = ref.left.key + ref.right.key // occurrence h.insert(ref) // insert new (joint) tree } return h.del() } Huffman’sAlgorithm:BeweisderKorrektheit Theorem:Huffman‘sAlgorithmuslieferteineop]maleKodierungbezüglichder Dateigröße. Beweisidee: 1. DerBaumistvollständig. 2. DiebeidenseltenstenZeichena,bsind]efsteBläHereinesop]malen Kodierungsbaums.(Fallsnicht,wärec]efstesBlaH,dannliefert VertauscheneinenbesserenCode) 3. ZeichenaufderselbenEbenekönnenvertauschtwerden;deshalbsinda undbBrüder 4. Induk]onüberdieAnzahlderHäufigkeiten(Buchstaben)n;wirzeigen dassdieSummedermitdenHäufigkeitengewichtetenPfadlängenin Huffman‘sBaumdiegleicheistwieineinemop]malenBaum. Induk]onsanfangn=2:esgibtnureinenBaum. Induk]onsschriH:betrachtedenBauma,b,c,...undgreifedabeiaufden Bauma+b,c,...zurück.(q.e.d.) BeschränkteOp]mierung Beispiele: – Pakteversand • Eingabe:NPaketeundMAuslierungsorte • Ziel:MinimieredieAuslierungszeit • Beschränkung(Constraint):XLastwagen – CPUalloca]on • Eingabe:NJobsmiteinerPriorität • Ziel:MaximieredenDurchsatz(gewichtetmitPriorität) • Beschränkung:XProzessoren,YSpeicher,ZZeit Con]nuousKnapsackProblem:Beispiel – Eingabe:NunabhängigeKapitelIhrerAbschlussarbeit – Ziel:maximieredenGehaltderArbeit(mancheKapitelsindnichtso gehaltvoll) – Prüfungsordnung:höchstens60Seiten – LöscheKapitel(oderTeilevonKapiteln)umdieszuerreichen. • GierigerAnsatz1: – Wählejeweilsdasgehaltvollstenochnichtberücksich]gteKapitel – Wennesnichtpasst,nehmenurdieerstenkSeiten,diepassen. Kapitel Seiten Gehalt 1 12 5 2 15 5 3 20 4 4 60 8 5 14 3 – IstdieLösungop]mal? Con]nuousKnapsackProblem:GierigerAnsatz2 • GierigerAnsatz2: – Wählejeweilsdasnochnichtberücksich]gteKapitelmitdemhöchsten GehaltproSeite – Wennesnichtpasst,nehmenurdieerstenkSeiten,diepassen. Kapitel Seiten Gehalt Gehalt/Seite 1 12 5 0.42 2 15 5 0.33 3 20 4 0.2 4 60 8 0.13 5 14 3 0.21 – IstdieLösungop]mal? Con]nuousKnapsackProblem:Rucksackpacken Kon'nuierlichesKnapsack-Problem • Gegeben:nObjekteundeinenRucksack(Knapsack)derKapazitätC. ObjektihatGewichtwiundentsprichtdemProfit(Wert)pi. • FindediejenigenWertexi,diedenmaximalenProfit n ∑ xi pi i=1 n unterderBedingung ∑ xi wi ≤ C, 0 ≤ xi ≤ 1 liefern. i=1 Knapsack-Problem • Dasnicht-kon]nuierlicheKnapsack-Problem(odereinfach Knapsack-Problem)ergibtsichunterderzusätzlichenBedingung xi ∈{0,1} • Fürdasnicht-kon]nuierlicheProblemgibteskeinenkorrektengierigen Algorithmus! GierigerAlgorithmusfürdaskon]nuierliche KnapsackProblem The input to the algorithm is the knapsack capacity C, and an array a of size n, each of whose entries specifies • an id (e.g., the first item might have id 1, etc.), • a profit p, • and a weight w. The output tells how much of each object to select to maximize the profit. Objects not selected do not appear in the output. The function sort sorts the array a in nonincreasing order of the ratio of profit to weight. Input Parameters: a,C continuous_knapsack(a, C) { n = a.last for i = 1 to n ratio[i] = a[i].p/a[i].w // Wert pro kg sort(a,ratio) // sortiere a nach absteigender ratio weight = 0 // Gesamtgewicht des Rucksacks i = 1 while (i ≤ n && weight < C) { // noch Platz? if (weight + a[i].w ≤ C) { // a[i] passt komplett? println(“select all of object ” + a[i].id) weight = weight + a[i].w } else { // a[i] passt nicht komplett r = (C - weight)/a[i].w println(“select ” + r + “ of object ” + a[i].id) weight = C } i = i + 1 Laufzeit: } Θ(nlogn)fürsSor]eren } Θ(n)fürloops KorrektheitvonconVnuous_knapsack() Theorem:conVnuous_knapsack()istkorrekt,d.h.liefertmaximalenProfit. Bw.:DasGesamtgewichtderObjekteist>C(sonsttrivial).SeiPdertotale Profit.BetrachteeinebeliebigeLösungmitxi’undP’.WirzeigenP’≤P. n n n ∑ xi 'wi ≤ C =∑ xi wi ⇒ 0 ≤ ∑ (xi − xi ')wi . i=1 i=1 i=1 SeikderkleinsteIndexmitxk<1. pi pk pi pk ⇒ ( xi − xi ' ) ≥ ( xi − xi ' ) Füri<k,xi=1,also: xi − xi ' ≥ 0 ∧ ≥ wi wk wi wk p p p p Füri>k,xi=0,also: xi − xi ' ≤ 0 ∧ i ≤ k ⇒ ( xi − xi ') i ≥ ( xi − xi ') k wi wk wi wk n n p Insgesamtalso: P − P ' = ∑ (xi − xi ')pi = ∑ (xi − xi ')wi i ≥ ... ≥ 0. qed wi i=1 i=1 Approxima]vesbin-packing(PackeninKisten) LösungensindnurNäherungen,abererwiesenermaßengute! Problem: GegebenseienNAr]kelderGrößes1,...,sNmit0<si≤1. WiefasstmandiesesoinKistenmitVolumen1zusammen,dassmandie minimaleAnzahlanKistenerhält? Bsp.:s=0.2,0.5,0.4,0.7,0.1,0.3,0.8(1-dimensional) Op]maleLösung:{0.8,0.2},{0.3,0.7},{0.5,0.1,0.4} Approxima]vesbin-packing(PackeninKisten) Online-Algorithmen:VerarbeitungderEingabeelementweise:Zuordnungzu einerKiste,keinenachträglichenKorrekturen. Kannkeineop]maleLösunggaran]eren: I1:MAr]kelderGröße½+e I2:MAr]kelderGröße½-e Op]mal:erstjedesElementausI1ineineKiste,dannI2dazu;beinurI1 schlecht. Theorem:EsgibtEingabenfürdasOnlineBin-Packing,dieLösungenmit≥4/3 Merzwingen,wennMdieop]maleKistenzahlist. Beweis:Annahme:esgibteinenbesserenAlgorithmus(*). I1:MAr]kelinbKisten,op]malistM/2,nach(*)b/(M/2)<4/3,alsob/M<2/3. I2dazu:AlleBinsnachbhabennur1Ar]kel(2ausI2passennicht),also insgesamt≥2M-bKistenfür2MAr]kel(op]malM), Nach(*)gilt(2M-b)/M<4/3,alsob/M>2-4/3=2/3.DasisteinWiderspruchzu oben,(*)mussalsofalschsein.qed