27.11.2012 Aufrufe

Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>Algorithmen</strong> <strong>und</strong><br />

<strong>Datenstrukturen</strong><br />

Kapitel 7:<br />

<strong>Algorithmen</strong>konstruktion II<br />

Sommersemester 2007<br />

Prof. Dr. Stefan Fischer


Inhalt<br />

� Dynamische Programmierung<br />

� Verteilte Berechnungen<br />

� Zufallsgesteuerte <strong>Algorithmen</strong><br />

2/47


Dynamische Programmierung<br />

� Gr<strong>und</strong>prinzip<br />

� Beispiel: Fibonacci-Zahlen<br />

� Das Rucksack-Problem<br />

� Textsuche (Vorberechnung)<br />

3/47


Gr<strong>und</strong>prinzip<br />

� Rekursive Lösungsstruktur:<br />

1. Aufteilung in abhängige Teilprobleme (Ggs: Teile-<strong>und</strong>-Herrsche<br />

mit unabhängigen Teilproblemen)<br />

2. Berechnen <strong>und</strong> Zwischenspeichern wiederkehrender<br />

Teilergebnisse<br />

3. Bestimmung des Gesamtergebnisses unter Verwendung der<br />

Teilergebnisse<br />

� Häufig verwendet bei Optimierungsproblemen:<br />

� Interpretation der Teilergebnisse als optimale Lösung eines<br />

"kleineren" Teilproblems<br />

� Ggf. zusätzlicher Schritt 4: Konstruktion der optimalen Lösung auf<br />

Basis der berechneten Teillösungen.<br />

Dabei ggf. Hinzuziehung weiterer Informationen aus Schritt 3.<br />

4/47


Beispiel (1/2)<br />

Beispiel Fibonacci-Zahlen:<br />

fib:IN 0 →IN mit<br />

fib(0) = 0<br />

fib(1) = 1<br />

n > 1: fib(n) = fib(n-1) + fib(n-2)<br />

Analoge Rekursive Lösung<br />

in Java Pseudocode:<br />

public int fibRek(int n) {<br />

if (n = 0) return 0;<br />

if (n = 1) return 1;<br />

return fibRek(n-1)+fibRek(n-2);<br />

}<br />

Berechnung wiederholt<br />

bestimmte Schritte:<br />

fib(2) = fib(1) + fib(0)<br />

fib(3) = fib(2) + fib(1)<br />

fib(4) = fib(3) + fib(2) ...<br />

Aufwandsabschätzung:<br />

T(n) = Anzahl Additionen<br />

T(0) = 0<br />

T(1) = 0<br />

T(n) = 1+T(n-1)+T(n-2) für n≥2<br />

=> Rekursive Lösung ist teuer!<br />

Es geht billiger mit dynamischer Programmierung.<br />

5/47


Beispiel (2/2)<br />

Iterative dynamische Lösung in Java Pseudocode:<br />

public int fibDynPro(int n) {<br />

int result;<br />

if (n = 0) return 0;<br />

if (n = 1) return 1;<br />

int fibMinus1 = 1, fibMinus2 = 0;<br />

for(int i=2; i


Das Rucksack-Problem (1/8)<br />

� Die Story:<br />

� Schatzsucher findet Schatz bestehend aus Edelsteinen.<br />

� Jeder Edelstein hat<br />

- ein bestimmtes Gewicht <strong>und</strong><br />

- einen bestimmten Wert.<br />

� Er hat nur einen Rucksack, dessen Kapazität durch ein maximales<br />

Gewicht gegeben ist.<br />

� Die Aufgabe: befülle den Rucksack mit Edelsteinen<br />

� ohne die Gewichtsbeschränkung zu verletzen<br />

� bei Maximierung des Wertes der Edelsteine.<br />

7/47<br />

x kg y €<br />

≤ z kg


Das Rucksack-Problem (2/8)<br />

� Formale Fassung des Rucksack-Problems (engl. Knapsack-P.):<br />

� Gegeben<br />

� Kapazität c ∈ IN<br />

� Menge O mit n∈ IN Objekten o 1 , o 2 ,..., o n<br />

� Gewichtsfunktion g:O→IN<br />

� Bewertungsfunktion w: O→IN<br />

� Gesucht ist O’⊆ O mit<br />

Dabei gelte ∑<br />

j∈<br />

O<br />

g ( j)<br />

><br />

c<br />

∑<br />

j∈O'<br />

g( j)<br />

≤ c <strong>und</strong> w(<br />

j)<br />

maximal<br />

∑<br />

j∈O'<br />

Anmerkung: Eigtl. 0-1 Knapsack Problem, da nur ganze Objekte<br />

betrachtet werden dürfen (ansonsten Fractional Knapsack Problem).<br />

8/47


Das Rucksack-Problem (3/8)<br />

� Erste Lösungsidee: Greedy<br />

� Aber: Greedy funktioniert nicht!<br />

� Beispiel: O = { o 1, o 2, o 3 }, Kapazität c = 5<br />

� Gewichte: g(o 1 ) = 1, g(o 2 ) = 2, g(o 3 ) = 3<br />

� Werte: w(o 1 ) = 6, w(o 2 ) = 10, w(o 3 ) = 12<br />

� Greedy: Nimm Objekt mit größtem relativen Wert bis Rucksack voll!<br />

� Relative Werte ( r(o)=w(o)/g(o) ): r(o 1) = 6, r(o 2) = 5, r(o 3) = 4<br />

� Ergebnis: O’={o 1, o 2}, mit<br />

∑<br />

j∈O'<br />

sicher nicht maximal!<br />

Besser ist O’’={o3 , o2 } m.∑<br />

j∈<br />

g( j)<br />

= 3 < c = 5 <strong>und</strong><br />

O''<br />

∑ ∈O<br />

j<br />

'<br />

w(<br />

j)<br />

= 16<br />

∑<br />

j∈O''<br />

g( j)<br />

= 5 ≤ c = 5 u. w(<br />

j)<br />

= 22<br />

9/47


Das Rucksack-Problem (4/8)<br />

� Zweite Lösungsidee: Backtracking<br />

� Funktioniert, ist aber aufwändig!<br />

� Beispiel: O = { o 1, o 2, o 3 , o 4 }, c = 10<br />

� Gewichte: g(o 1 ) = 2, g(o 2 ) = 2, g(o 3 ) = 6, g(o 4 ) = 5<br />

� Werte: w(o 1 ) = 6, w(o 2 ) = 3, w(o 3 ) = 5, w(o 4 ) = 4<br />

� Lösungsraum<br />

(Konfigurations-<br />

baum):<br />

10 8<br />

8<br />

6<br />

(0,0) (5,4) (6,5) (2,3) (7,7) (8,8) (2,6) (7,10) (8,11) (4,9) (9,13) (10,14)<br />

10/47<br />

Zur Disposition:<br />

o :(g,w)<br />

10 4 8 2 8 2 6 0<br />

10 5 4 8 3 2 8 3 2 6 1 0<br />

*Restkapazität<br />

10*<br />

nein<br />

ja<br />

10 8<br />

Teilbäume gleich<br />

> Optimierungspotenzial<br />

> Dyn. Prog<br />

o 1 :(2,6)<br />

o 2 :(2,3)<br />

o 3 :(6,5)<br />

o 4 :(5,4)


Das Rucksack-Problem (5/8)<br />

Rekursive Backtracking-Lösung in Java Pseudocode:<br />

public int btKnapsack(int i, int restkapazität ) { // Aufruf: btKnapsack(1,c)<br />

// Rückgabewert: Wert d. optimalen Füllung bei O={oi,...,on} <strong>und</strong><br />

c=restkapazität<br />

Objekt o=objekte[i]; // objekte[] ist globale Variable mit Indexmenge {1,2,....,n}<br />

if ( i==objekte.length ) // Ende der Rekursion: o ist das letzte Objekt<br />

if (o.gewicht() > restkapazität) // wenn o nicht mehr hinein passt, dann<br />

return 0; // bleibt es draußen<br />

else return o.wert(); // andernfalls kommt es hinein <strong>und</strong> steigert den Wert<br />

else if (o.gewicht() > restkapazität) // analog bei den anderen Objekten<br />

return btKnapsack(i+1, restkapazität)<br />

else return max( btKnapsack(i+1,restkapazität),<br />

btKnapsack(i+1,restkapazität–o.gewicht())+o.wert() ); //<br />

Wenn es noch hinein passt, werden dennoch<br />

} // beide Möglichkeiten betrachtet<br />

Anmerkung: Optimierungspotenzial (Ausnutzen wiederkehrender Berechnungen)<br />

wird hier nicht genutzt!<br />

11/47


Das Rucksack-Problem (6/8)<br />

� Lösung durch dynamische Programmierung<br />

� Beispiel: O = { o 1 , o 2 , o 3 , o 4 , o 5 }, c = 10<br />

� Gewichte: g(o 1 ) = 2, g(o 2 ) = 2, g(o 3 ) = 6, g(o 4 ) = 5, g(o 5 ) = 4<br />

� Werte: w(o 1 ) = 6, w(o 2 ) = 3, w(o 3 ) = 5, w(o 4 ) = 4 , w(o 5 ) = 6<br />

� Idee: In Iteration rückwärts die Resultate der Aufrufe btKnapsack(i,r)<br />

berechnen > Verwendung eines zweidimensionalen Feldes f:<br />

i<br />

r<br />

0 1 2 3 4 5 6 7 8 9 10<br />

5 0 0 0 0 6 6 6 6 6 6<br />

4 0 0 0 0 6 6 6 6 6 10 10<br />

3 0 0 0 0 6 6 6 6 6 10 11<br />

2 0 0 3 3 6 6 9 9 9 10 11<br />

Beispiel*: f[4,9]=btKnapsack(4,9)=10 (d.h. o 4 <strong>und</strong> o 5 haben bei<br />

Restkapazität 9 noch Platz: Wert der Füllung = 10)<br />

*Anstelle der Java-üblichen Schreibweise f[4][9] wird im Foglenden f[4,9] verwendet.<br />

6<br />

12/47


Das Rucksack-Problem (7/8)<br />

� Beachte zentrale Anweisungen in btKnapsack(i,r):<br />

� ... else return max( btKnapsack(i+1,restkapazität),<br />

�<br />

Bsp.:<br />

btKnapsack(i+1,restkapazität–o.gewicht())+o.wert() ) ...<br />

0 1 2 3 4 5 6 7 8 9 10<br />

5 0 0 0 0 6 6 6 6 6 6<br />

4 0 0 0 0 6 6 6 6 6 10 10<br />

3 0 0 0 0 6 6 6 6 6 10 11<br />

2 0 0 3 3 6 6 9 9 9 10 11<br />

f[3,8] = max(f[4,8], f[4,2]+5) = max(6,0+5) = 6<br />

Idee für dynamische Programmierung verfeinert:<br />

1. Berechnung der Werte für i=n <strong>und</strong> alle Restkapazitäten von 0 bis c.<br />

2. Dann von i=n-1 bis 2 Berechnung der Feldeinträge für jeweils alle Kapazitäten<br />

unter Rückgriff auf die bereits berechneten Werte der Zeile i+1.<br />

3. Gesamtergebnis f[1,c] dann durch Einzelberechnung<br />

(f[1,10] = max(f[2,10], f[2,8]+6) = max(11,9+6) = 15).<br />

6<br />

13/47


Das Rucksack-Problem (8/8)<br />

Dynamische Programmierung in Java Pseudocode:<br />

public int dpKnapsack(int n, int c ) {// Aufruf: dpKnapsack(objekte.length,c)<br />

int[][] f = new int[2,n][0,c]; // Deklaration des Feldes (CAVE: Java-unüblich)<br />

// Initialisierung des Feldes für i = n<br />

for (int r = 0; r r ) f[n,r] = 0;<br />

else f[n,r] = objekte[n].wert();<br />

// Berechnung des Feldes für i = n-1,...,2<br />

for (int i = n-1; i >=2; i = i-1)<br />

for (int r = 0; r r ) f[i,r] = f[i+1,r];<br />

else f[i,r] = max( f[i+1,r], f[i+1, r- objekte[i].gewicht()]<br />

+objekte[i].wert() );<br />

// Berechne f[1,c] (den maximal erreichbaren Wert)<br />

if ( objekte[1].gewicht() > c ) return f[2,c];<br />

else return max( f[2,c], f[2, c-objekte[1].gewicht()] + objekte[1].wert() );<br />

}<br />

14/47


Textsuche<br />

� Problem: Finden eines Wortes in einem (längeren) Text.<br />

� Konkret:<br />

� Gegeben:<br />

- Array a von Zeichen der Länge n<br />

(Bsp.: a="Eine Rose ist eine Rose ist eine Rose")<br />

- Array s von Zeichen der Länge m (i.d.R. m


Textsuche – Brute-Force-Lösung<br />

� Brute-Force-Lösung:<br />

� Suche ersten übereinstimmenden Buchstaben (Laufindex i).<br />

� Falls gef<strong>und</strong>en an Position i=j: vergleiche nachfolgende Buchstaben<br />

� Falls erfolgreich: Ende <strong>und</strong> Rückgabe von i<br />

� Falls nicht erfolgreich: erhöhe i um 1 <strong>und</strong> mache weiter mit Schritt 1<br />

Konkret (Java-Pseudocode):<br />

public int bruteForceSearch( char[] a, char[] s ) {<br />

int k; // Laufvariable<br />

for (int i = 0; i


Textsuche - Beispiel<br />

a<br />

s<br />

0 1 2 3 4 5 6 7<br />

8 9 10 11 12 13 14 15 ...<br />

E i n e R o s e i s t e i<br />

R o s e<br />

i=0 (k=0) R o s e<br />

i=1 (k=0) R o s e<br />

i=2..3 (k=0) ...<br />

i=4 (k=0) R o s e<br />

i=5 (k=0) R o s e<br />

i=5 (k=1) R o s e<br />

i=5 (k=2) R o s e<br />

s.length=4 // ...relevanter Teil:<br />

for (int i = 0; i


Textsuche – Laufzeit bei Brute-Force<br />

� Maßstab für Laufzeit:<br />

Anzahl Vergleiche<br />

� Worst-Case:<br />

� Auftreten Worst-Case:<br />

Wenn ab jeder Position in a die Zeichenkette s bis auf das letzte<br />

Zeichen auftritt.<br />

Beispiel: a="aaaaaaaaaaaaaaaaaab" s="aaab"<br />

=> (3) wird bei jedem Zeichen aus a betreten (d.h. n-m Mal)<br />

<strong>und</strong> vollständig (d.h. m Mal) durchlaufen<br />

� Anzahl Vergleiche: (n-m)*m<br />

� Laufzeit: O((n-m)*m) = O(nm)<br />

� Gewünscht: Verbesserung der Laufzeit insbesondere für große Muster<br />

bzw. für große s.<br />

(1)<br />

(2)<br />

(3)<br />

(4)<br />

// Relevanter Teil:<br />

... for (int i = 0; i


Textsuche – Verbesserung<br />

Verschieben des Suchmusters bei Misserfolg des Vergleichs nicht um 1<br />

sondern um Entfernung e<br />

Problem: e hängt vom Muster s ab<br />

Beispiel 1:<br />

a="123123x123" <strong>und</strong> s="123x" (alle Zeichen verschieden)<br />

=> Bei Misserfolg kann bis zur Stelle des Mismatch verschoben<br />

werden.<br />

Beispiel 2:<br />

a=„345345345x123" <strong>und</strong> s=„345345x" (Teilmuster wiederholt sich)<br />

=> bei Misserfolg im 7‘ten Vergleich (k=6) muss a mit s ab Position<br />

4 von s verglichen werden (i=3 <strong>und</strong> k=3).<br />

Ablauf: i=0 > Übereinstimmung 0.-5. Zeichen > Fehler bei<br />

k=6 > i=i+3 ...<br />

19/47


Algorithmus von Knuth-Morris-Pratt(1/5):<br />

� Idee: Für jedes Zeichen s[k] im Muster merken, um wie viele<br />

Positionen bei Misserfolg des Vergleiches a[i+k]==s[k] in a<br />

vorangeschritten werden kann.<br />

� Hilfsstruktur hierfür: int[] dis = new int[s.length]<br />

� Achtung: Index i wird mit k inkrementiert. Mittels dis[] wird k<br />

korrigiert!<br />

Beispiel:<br />

a<br />

0 1 2 3 4 5 6 7<br />

dis -1 0 0 0 1 1<br />

i=0..4, k=0..4 s A B C A A B<br />

i=4..5, k=1..2<br />

i=5..7, k=0..2<br />

8 9 10 11 12 13 14<br />

A B C A B A B A B C A A B A B<br />

A B C A A B<br />

Korrektur: k=1<br />

A B C A A B<br />

Korrektur: k=0<br />

Korrektur: k=0<br />

i=7..12, k=0..5 A B C A A B Rückgabe: i-k=7<br />

20/47


Algorithmus von Knuth-Morris-Pratt(2/5):<br />

� Problem: Bestimmen des Distanzfeldes dis.<br />

� Notwendig: Überprüfung des Musters auf sich wiederholende<br />

Teilmuster s[0]...s[j]. Findet sich eine Wiederholung des Umfanges j≥0<br />

links vom kritischen Vergleichsindex k, d.h. ist s[k-1-j] ...s[k-1] gleich<br />

s[0]...s[j], so kann der Vergleich s[0]...s[j] ausgelassen werden. D.h. k<br />

kann gleich j+1 gesetzt werden.<br />

Beispiel:<br />

a<br />

0 1 2 3 4 5 6 7<br />

i=0..4, k=0..4 s A B C A A B<br />

8 9 10 11 12 13 14<br />

A B C A B A B A B C A A B A B<br />

s[0]...s[0] ist gleich s[k-1-0]...s[k-1]=s[3]<br />

=> Verschieben auf k = j+1 = 0+1 = 1<br />

s A B C A A B<br />

k =1 ist zugleich der Index für<br />

den nächsten Vergleich!<br />

21/47


Algorithmus von Knuth-Morris-Pratt (3/5):<br />

� Lösungsidee zur Bestimmung des Distanzfeldes dis (grob):<br />

Eine Kopie des Musters wird am Muster selbst vorbei<br />

geschoben, wobei die überlappenden Zeichen verglichen<br />

werden. In dis werden die jeweils übereinstimmenden<br />

Zeichen gespeichert.<br />

Beispiel:<br />

s<br />

0 1 2 3 4 5<br />

A B C A A B<br />

dis[i]<br />

i=1 s A B C A A B 0<br />

i=2 s A B C A A B 0<br />

i=3 s<br />

A B C A A B 0<br />

i=4 s<br />

A B C A A B 1<br />

i=5 s<br />

A B C A A B 1<br />

dis[i]= "0 oder x+1, falls ∃x:<br />

s[i-1-x]...s[i-1]=s[0]...s[x]"<br />

Bei Ungleichheit nach rechts<br />

verschieben um ein Feld<br />

Verschieben stoppt bei<br />

Gleichheit!<br />

Bei erster Ungleichheit Verschieben<br />

wieder aufnehmen!<br />

22/47


Algorithmus von Knuth-Morris-Pratt (4/5):<br />

Bestimmung des Distanzfeldes in Java Pseudocode:<br />

public int[] initDis(char[] s ) {<br />

int[] dis = new int[s.length]; // Deklaration des Feldes<br />

int i = 0, j = -1; // Deklaration/Initialisierung temporärer Laufvariablen<br />

dis[0] = -1; // Init. dis[0]<br />

// i „durchwandert“ das Muster von links (0) nach rechts bis s.length-2<br />

while (i < s.length-1) {<br />

// j „zählt“ die gleichen Anfangszeichen<br />

while (j >= 0 && s[i] != s[j]) j=dis[j]; // Schleifenrumpf nur bei s[i]≠s[j] relevant<br />

// hiernach ist s[i]=s[j];<br />

//nur solange werden j <strong>und</strong> i inkrementiert u. dis[i] gesetzt<br />

i=i+1; j=j+1;<br />

dis[i] = j;<br />

}<br />

return dis;<br />

}<br />

23/47


Algorithmus von Knuth-Morris-Pratt (5/5):<br />

Nun zur eigentlichen Suchfunktion in Java Pseudocode:<br />

public int kmpSearch(char[] a, char[] s ) {<br />

int[] dis = initDis(s); // Berechnung des Distanzfeldes<br />

int i = 0, j = 0; // Deklaration/Initialisierung temporärer Laufvariablen<br />

// i „durchwandert“ den Text von links (0) nach rechts bis a.length-1<br />

while (i < a.length) {<br />

// j „zählt“ die gleichen Anfangszeichen<br />

while (j >= 0 && s[j] != a[i]) j=dis[j]; // Schleifenrumpf nur bei a[i]≠s[j] relevant<br />

// hiernach ist a[i]=s[j]; nur solange werden j <strong>und</strong> i inkrementiert<br />

i=i+1; j=j+1;<br />

if (j==s.length) return i-s.length;<br />

}<br />

return -1;<br />

}<br />

24/47


Anmerkungen<br />

� Laufzeit von kmpSearch() :<br />

� Laufzeit initDis(): Maximal 2m Durchläufe => O(m)<br />

� Laufzeit kmpSearch(): O(m)+“Maximal 2n Durchläufe“<br />

� O(n+m)<br />

� Vorteil von kmpSearch() entfaltet sich erst bei sich<br />

wiederholenden Textmustern im Suchstring.<br />

� Weiterer Vorteil: In der zu durchsuchenden Zeichenkette<br />

muss nie zurückgegangen werden (gut bei externen<br />

Speichermedien).<br />

� Was hat all dies mit dynamischer Programmierung zu tun:<br />

das hier eingesetzte Prinzip der Vorberechnung ist eine<br />

Spezialisierung der dynamischen Programmierung.<br />

� „Variante“ von kmpSearch(): Boyer-Moore<br />

25/47


Inhalt<br />

� Dynamische Programmierung<br />

� Verteilte Berechnungen<br />

� Zufallsgesteuerte <strong>Algorithmen</strong><br />

26/47


Verteilte Berechnungen<br />

� Analogon Hausbau:<br />

� Schritte:<br />

� Baugrube ausheben<br />

� Bodenplatte gießen<br />

� Mauern & Dach decken<br />

� Fenster einbauen<br />

� Türen einbauen<br />

� Tapezieren<br />

� ...<br />

� Abhängigkeiten sind<br />

einzuhalten!<br />

=> Paralleles Arbeiten<br />

erfordert Koordination<br />

Prinzipiell durch<br />

eine Person möglich<br />

=> Dauert<br />

(sehr) lange<br />

Besser:<br />

Teamwork<br />

=> Schnell, da<br />

parallel gearbeitet<br />

werden kann<br />

27/47


Parallelisierung/Sequenzialiserung<br />

� Lässt sich ein Problem in unabhängige Teilprobleme<br />

zerlegen, so bietet sich zwecks Steigerung der Effizienz die<br />

unabhängige Berechnung der Teilprobleme durch<br />

getrennte Prozessoren an (Parallelisierung):<br />

Nach Bearbeitung der Teilprobleme: Zusammenführen<br />

der Ergebnisse (Sequenzialisierung).<br />

...<br />

28/47


Prozesse<br />

� Zentraler Begriff: der Prozess (=Vorgang einer<br />

algorithmisch arbeitenden Informationsbearbeitung)<br />

� Konzept: Pro Zeiteinheit wird auf jedem Prozessor genau<br />

ein Prozess ausgeführt.<br />

� Prozesse haben Lebenszyklus (vereinfacht):<br />

� sie werden gestartet <strong>und</strong> laufen ab,<br />

� sie werden blockiert <strong>und</strong> warten (z.B. auf Eingabe),<br />

� sie werden wiederbelebt <strong>und</strong> laufen ab <strong>und</strong><br />

� sie werden beendet <strong>und</strong> bleiben terminiert.<br />

� Ein Prozess kann sog. Kind-Prozesse anstoßen.<br />

Notwendig: Kommunikation von Prozessen<br />

(über Status des (Kind-)Prozesses, bzgl. Daten (z.B.<br />

Rechenergebnisse) etc.)<br />

suspend<br />

initiated<br />

running<br />

waiting<br />

start<br />

terminated<br />

29/47<br />

resume<br />

kill


Realisierung<br />

� Varianten (grob):<br />

� Mehrere Prozessoren befinden sich auf einer Platine<br />

(Mehrprozessorsystem)<br />

� In einem Netzwerk von Rechnern steht pro Rechner<br />

ein Prozessor zur Verfügung (Cluster)<br />

Kombinationen sind möglich!<br />

Prinzip: Ein Prozess gibt lediglich an, ob <strong>und</strong> welche<br />

Kind-Prozesse erzeugt <strong>und</strong> gestartet werden<br />

sollen. Die Verteilung <strong>und</strong> Durchführungskontrolle<br />

(inkl. Nachrichtenvermittlung) übernimmt das<br />

(Netzwerk-) Betriebssystem (abgek. BS).<br />

Multiprocessingsysteme ermöglichen virtuelle<br />

Parallelität auf nur einem Prozessor: Hier verteilt<br />

das BS Rechenzeit(scheiben) auf Prozesse.<br />

Windows 2000<br />

UNIX ...<br />

30/47


Modellierung verteilter Berechnung (1/3)<br />

� Petri-Netze (C.A. Petri 1962) dienen der Modellierung<br />

sogenannter nebenläufiger <strong>und</strong> nichtdeterministischer Abläufe.<br />

� Gr<strong>und</strong>elemente dieser gerichteten Graphen:<br />

1. Transitionen: stellen elementaren Arbeitsschritt dar<br />

2. Stellen: Zwischenlager für "Datenobjekte„<br />

zerlegen<br />

3. Marken: Datenobjekt/Kontrollfluss<br />

Teilproblem 1<br />

Bedingung: Nur zwischen Stellen <strong>und</strong> Transitionen (<strong>und</strong> umgekehrt)<br />

bestehen Verbindungen.<br />

Idee: Transitionen verarbeiten die Marken der Stellen ihres<br />

Vorbereichs <strong>und</strong> geben die Resultate an Stellen im Nachbereich ab<br />

Unsortierte Liste<br />

Sortieren<br />

t→t+1<br />

Sortierte Liste<br />

Unsortierte Liste<br />

Sortieren<br />

Sortierte Liste<br />

31/47


Modellierung verteilter Berechnung (2/3)<br />

� Petri-Netze formal (Definition):<br />

� Ein Petrinetz ist ein 4-Tupel (S,T,F,M) mit:<br />

� S≠∅ (Menge von Stellen)<br />

� T≠∅ ∧ S∩T=∅ (von S unterschiedliche Menge von Transitionen)<br />

� F⊆S×T ∪ T×S (Flussrelation zw. Stellen <strong>und</strong> Transitionen u.u.)<br />

� M: S→IN 0 (Startmarkierung der Stellen)<br />

� Weitere Definitionen:<br />

� •t = {s∈S|(s,t) ∈F} für t∈T<br />

(Vorbereich einer Transition)<br />

Beispiel:<br />

� t• = {s∈S|(t,s) ∈F} für t∈T<br />

(Nachbereich einer Transition) •t1={s1,s2}<br />

t1• ={s2,s3}<br />

s1<br />

s3<br />

s2<br />

t1<br />

32/47


Modellierung verteilter<br />

Berechnung (3/3)<br />

� Ausführbarkeit einer Transition t: Gilt für eine Markierung<br />

M': S→IN 0, dass ∀s ∈ •t : M'(s)≥1, so ist t ausführbar.<br />

� Semantik: Folge von Markierungen > Schaltregel<br />

� Schaltregel: Eine Markierung M': S→IN 0 kann aus einer Markierung M<br />

wie folgt hervorgehen:<br />

Beispiel:<br />

•t1={s1,s2}<br />

t1• ={s2,s3}<br />

M(s1)=3, M(s2)=1,<br />

M(s3)=1<br />

(=> t1 ausführbar)<br />

M'(s)=<br />

s1<br />

s3<br />

s2<br />

t1<br />

M(s)+1 falls t ausführbar, s∈t• aber s∉•t<br />

M(s)-1 falls t ausführbar, s∈•t aber s∉t•<br />

M(s) sonst<br />

M»M'<br />

s1<br />

s3<br />

s2<br />

t1<br />

M'(s1)=2,<br />

M'(s2)=1,<br />

M'(s3)=2<br />

33/47


Petri-Netze – Gr<strong>und</strong>legende Strukturen (1/2)<br />

• Sequentieller Ablauf Transition 1 Transition 2<br />

• Parallelisierung<br />

• Synchronisation<br />

(Sequenzialisierung)<br />

Transition 1<br />

Transition 1<br />

Transition 2<br />

Transition 2<br />

Transition 3<br />

Transition 3<br />

34/47


Petri-Netze – Gr<strong>und</strong>legende Strukturen (2/2)<br />

• Alternative<br />

Achtung !<br />

Transition 1<br />

Markenfluß ist nicht deterministisch!<br />

• Schleife<br />

Transition 2<br />

Transition 3<br />

Transition 3<br />

Transition 1 Transition 2<br />

35/47


Beispiel: Hausbau 2<br />

Baugrube ausheben<br />

Grube<br />

ausgehoben<br />

Bau genehmigt<br />

F<strong>und</strong>ament giessen<br />

Keller mauern<br />

Bodenplatte giessen<br />

F<strong>und</strong>ament gegossen<br />

Türen sind<br />

einzubauen<br />

Türen<br />

eingebaut<br />

Mauern möglich<br />

Türen einbauen<br />

Mauern & Dach decken<br />

Haus fertig<br />

Fenster eingebaut<br />

Tapezieren<br />

Eigentlich Spezialfall von Petri-Netz: Bedingungs-/Ereignisnetz (> Kontrollfluss)<br />

Fenster<br />

sind<br />

einzubauen<br />

36/47<br />

Fenster einbauen


Weitere Aspekte<br />

� Erweiterungen von Petri-Netzen:<br />

� Stellen erhalten Kapazität (maximale Markenanzahl)<br />

� Beziehungen erhalten Gewicht (wird bei der Berechnung der<br />

Folgemarkierung anstelle "1" verwendet)<br />

� Marken werden gefärbt<br />

� u.s.w. (> Theoretische Informatik)<br />

� Petri-Netze (oder verwandte Beschreibugen) werden<br />

häufig für Prozessmodellierung eingesetzt; oft auch nur zur<br />

Veranschaulichung.<br />

� Potentielle Probleme nebenläufiger Prozesse:<br />

Deadlocks/Livelocks<br />

� Wichtiges Hilfsmittel für Koordination von Prozessen:<br />

Semaphoren (Dijkstra) > Regelung des Zugriffs auf<br />

gemeinsame Ressource<br />

37/47


Inhalt<br />

� Dynamische Programmierung<br />

� Verteilte Berechnungen<br />

� Zufallsgesteuerte <strong>Algorithmen</strong><br />

38/47


Zufallsgesteuerte <strong>Algorithmen</strong><br />

� Gr<strong>und</strong>prinzip<br />

� Gr<strong>und</strong>muster<br />

� Monte Carlo <strong>Algorithmen</strong><br />

� Las Vegas <strong>Algorithmen</strong><br />

� Allgemeiner Las Vegas Algorithmus<br />

� Macao Algorithmus<br />

39/47


Gr<strong>und</strong>prinzip<br />

� Idee: Verwendung zufällig gewählter Daten, um die<br />

Laufzeit eines Algorithmus zu senken.<br />

� Einige Aufgaben sind heute überhaupt nur<br />

auf diese Weise effizient zu lösen.<br />

� Beispiel: Primzahleigenschaft großer Zahlen<br />

� Zentrale Eigenschaft zufallsgesteuerter <strong>Algorithmen</strong>: sie<br />

sind nicht deterministisch.<br />

� Zentrales Werkzeug zufallsgesteuerter <strong>Algorithmen</strong>:<br />

� Zufallszahlengeneratoren<br />

� Beispiel (Java): public static double random() der Klasse Math<br />

realisiert eine im Intervall [0,1[ gleichverteilte Zufallsvariable<br />

� S.a. Klasse Random<br />

40/47


Gr<strong>und</strong>muster für den Entwurf<br />

� Auswahl: aus einer Menge deterministischer <strong>Algorithmen</strong><br />

zur Lösung eines Teilproblems wird zufällig einer<br />

ausgewählt<br />

� Stichprobe: aus einer zufällig gewählten Stichprobe von<br />

Daten wird auf Eigenschaft aller Daten geschlossen<br />

� Zeuge (Betrifft Ja/Nein-Entscheidungen): Auf hinreichend<br />

viele Daten (die Zeugen) angewendet bestätigen<br />

deterministische Alg. ein Ereignis mit Wahrscheinlichkeit<br />

p>0,5 (>Monte Carlo)<br />

� Umordnung: Eingabemenge wird zufällig umgeordnet<br />

(>etwaige für Laufzeit negative Trends zerstören)<br />

41/47


Monte Carlo <strong>Algorithmen</strong><br />

� Prinzip: Monte Carlo <strong>Algorithmen</strong> (MCA) liefern nur mit einer<br />

Wahrscheinlichkeit p Bei Entscheidungsproblemen: p≠0,5<br />

� Gr<strong>und</strong>muster:<br />

� Ein MCA y:=mc(x) liefert ein korrektes Ergebnis nur mit<br />

Wahrscheinlichkeit p


MCA: Beispiel<br />

� Berechnung der Zahl π.<br />

� Formel für Kreisfläche: A= πr²<br />

� Betrachte Einheitskreis im 1. Quadranten<br />

Seien x <strong>und</strong> y Zufallszahlen aus [0,1[.<br />

⇒ p:=P( (x, y)∈A' )=A'/1= π/4<br />

1<br />

A'<br />

⇒A'= πr²/4= π/4<br />

Fläche Quadrat=1<br />

Wird dieses Experiment n Mal wiederholt <strong>und</strong> "landen" c Punkte (x,y)<br />

in A', so gilt π(n)=4c/n<br />

Beispiel: In einem Zufallsexperiment mit 10000 gleichverteilten<br />

Punkten (x,y) ∈ [0,1[×[0,1[ erfüllen 7874 Punkte die Bedingung x²+y² π(10000)=4*7874/10000=3.1496 (für den Fehler gilt: εMC-Integration)<br />

1<br />

43/47


Las Vegas <strong>Algorithmen</strong><br />

� Prinzip: Im Ggs. zu MCA liefert ein Las Vegas Algorithmus (LVA) immer das<br />

richtige Ergebnis, wenn er terminiert.<br />

� Unterscheidung:<br />

� Er terminiert mit Wahrscheinlichkeit presult) <strong>und</strong> dabei Zufallszahlen verwendet. Sie testet zudem,<br />

// ob das Ergebnis richtig ist <strong>und</strong> gibt das Resultat zurück (>erfolg)<br />

return result;<br />

}<br />

44/47


Allg. LVA: Eigenschaften/Beispiel<br />

� Es gibt keine obere Schranke für die Laufzeit des allgemeinen LVA.<br />

� Gr<strong>und</strong>: Im Extremfall werden unendlich viele schlechte Ergebnisse<br />

geraten <strong>und</strong> immer wieder verworfen.<br />

� Daher Forderung: der ungünstigste erwartete Fall ist immer endlich.<br />

� Beispiel: Zufälliges Färben von Elementen<br />

� Gegeben: Menge S mit n Elementen, paarweise verschiedene<br />

Untermengen S0,...,Sk-1⊆S mit |Si| = r > 0. Es gelte k≤2r-2.<br />

� Gesucht: Färbung der Elemente von S derart, dass jedes Si wenigstens ein<br />

rotes <strong>und</strong> ein schwarzes Element enthält.<br />

� Algorithmus: suggestResultSimple() ordnet jedem Element zufällig<br />

irgendeine Farbe zu <strong>und</strong> gibt zurück, ob erfolgreich.<br />

� Offensichtlich: Nicht immer korrekte Lösung.<br />

� Frage: Wahrscheinlichkeit für falsche Lösung?<br />

45/47


Allg. LVA: Beispiel Fortsetzung<br />

Frage: Wahrscheinlichkeit für falsche Lösung?<br />

P(ein Element wird rot gefärbt)<br />

= P(ein Element wird schwarz gefärbt) = 1/2<br />

P(alle Elemente von Si sind rot) = 1/ 2r = 2-r<br />

P(irgend ein Si ist vollständig rot gefärbt) ≤ k*2-r<br />

Voraussetzung: k≤2r-2<br />

Damit: P(irgend ein Si ist vollständig rot gefärbt) ≤ k*2-r ≤ 2r-2*2-r = 1/4<br />

=> P(irgend ein Si ist einheitlich (rot oder schwarz) gefärbt) ≤ 1/2<br />

=> suggestResultSimple() liefert<br />

mit einer Wahrscheinlichkeit ≤ 1/2 eine falsche Lösung <strong>und</strong><br />

mit einer Wahrscheinlichkeit ≥ 1-1/2=1/2 eine korrekte Lösung<br />

(Voraussetzung für allg. LVA erfüllt)<br />

46/47


Macao <strong>Algorithmen</strong><br />

� Macao <strong>Algorithmen</strong> entstehen meist aus gewöhnlichen<br />

deterministischen <strong>Algorithmen</strong>, indem<br />

� die Elemente der Eingabemenge in zufälliger Folge bearbeitet<br />

werden, oder<br />

� ein Zufallsgenerator genutzt wird, um ein Element zufällig zu<br />

wählen.<br />

� Beispiel für 1.: randomizedInputArrayQuickSort()<br />

� Beispiel für 2.: quickSort() mit determinePivotElement()-<br />

Dienst, der auf zufälliger Auswahl des Pivot-Elements<br />

beruht<br />

47/47

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!