Algorithmen und Datenstrukturen
Algorithmen und Datenstrukturen
Algorithmen und Datenstrukturen
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