Problemlösungsmuster "Teile und herrsche" am Beispiel "Sortieren

Werbung
Problemlösungsmuster "Teile und herrsche"
am Beispiel "Sortieren"
Annahme bei allen folgenden Verfahren:
die zu sortierende Menge hat genügend Platz im Hauptspeicher.
Zum Vergleich:
Einfachster Algorithmus ohne Anwendung von Teile und herrsche:
Vergleiche je zwei Elemente:
{ für i=a.length-1 …1,
für j=0..i-1
wenn a[j] < a[j+1] tausche a[j] und a[j+1]}
Animationen findet man hier: http://www.tcs.ifi.lmu.de/~gruberh/lehre/sorting/sort.html
hs / fub - alp2-12 76
15 3 10 22 7 1 5
3 15 10 22 7 1 5
3 10 15 22 7 1 5
3 10 15 7 22 1 5
3 10 15 7 1 22 5
3 10 15 7 1 5 22
3 10 7 15 1 5 22
3 10 7 1 15 5 22
3 10 7 1 5 15 22
3 7 10 1 5 15 22
3 7 1 10 5 15 22
3 7 1 5 10 15 22
3 1 7 5 10 15 22
3 1 5 7 10 15 22
1 3 5 7 10 15 22
Bubble Sort:
INV: a[i]..a[n-1] sorted
public void bubbleSort (double [ ] v){
// exchange the values in the vector v
// so they appear in ascending order
int n = v.length;
// find the largest remaining value
// and place into v[i]
for (int i = n - 1; i > 0; i--) {
// move large values to the top
for (int j = 0; j < i; j++) {
// if out of order
if (v[j] > v[j+1]) {
swapDouble(j,j+1,v)
}}
}
}
"Blasen" steigen auf, "schwere" Elemente sinken
hs / fub - alp2-12 77
Teile und Herrsche und Sortieralgorithmen
verbinden
zerlegen und lösen
> mergeSort [] = []
> mergeSort [x] = [x]
> mergeSort xs = merge (mergeSort left) (mergeSort right)
>
where left = take n xs
>
right = drop n xs
>
n = (length xs) `div` 2
>
merge xs [] = xs
>
merge [] ys = ys
>
merge (x:xrest) (y:yrest)
>
| x <= y = x:(merge xrest (y:yrest))
>
| y < x
= y:(merge (x:xrest) yrest)
hs / fub - alp2-12 78
Beispiel
Mischsortieren
(Merge sort)
15 3 10 22 7 1 5
715
15 3 10 22
10 22
15 3
15
10
3
22
71
7
10
22
3
15
3
10
15
22
5
1
Teile bis
Problem trivial
Füge Lösungen
zusammen
1
7
1
5
7
1 3 5 7 10 15 22
hs / fub - alp2-12 79
Mischsortieren
//pre: double [] a, N=a.length > 0, ∀i, 0≤i ≤N-1: a[i] definiert
double [] mergeSort(double[] a) {
if (a.length == 1) {
return a;
} else {
double[] firstHalf = mergeSort (firstHalf(a));
assert(for(int i=0; i<firstHalf.length-1;i++)
firstHalf[i] <= firstHalf[i+1];)
double[] secHalf =
mergeSort (secHalf(a));
assert ..
merge (firstHalf, secHalf);
}
}
Nachteil der funktionalen Lösung: viel Kopieroperationen
hs / fub - alp2-12 80
Variante mit einem temporären Hilfsspeicher (*)
public void mergeSort( double[] a )
{
double[] tmpArray = new double[ a.length ];
mergeSort( a, tmpArray, 0, a.length - 1 );
}
private
void mergeSort( double[ ] a, double[ ] tmpArray,
int left, int right ){
// Sort a in range a[left]..a[right]
if( left < right ){
int center = ( left + right ) / 2;
mergeSort( a, tmpArray, left, center );
mergeSort( a, tmpArray, center + 1, right );
merge( a, tmpArray, left, center + 1, right );
}
}
(*) Ohne Hilfsspeicher schwieriger – Mischen in situ!
hs / fub - alp2-12 81
Hauptarbeit: Mischen
private void merge( double[ ] a, double[ ] tmpArray,
int leftPos, int rightPos, int rightEnd ){
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
// Main loop
while( leftPos <= leftEnd && rightPos <= rightEnd )
if( a[leftPos]<= a[ rightPos ] )
tmpArray[ tmpPos++ ] = a[ leftPos++ ];
else
tmpArray[ tmpPos++ ] = a[ rightPos++ ];
// Copy rest of first half
while( leftPos <= leftEnd )
tmpArray[ tmpPos++ ] = a[ leftPos++ ];
while( rightPos <= rightEnd ) // Copy rest of right half
tmpArray[ tmpPos++ ] = a[ rightPos++ ];
// Copy tmpArray back
for( int i = 0; i < numElements; i++, rightEnd-- )
a[ rightEnd ] = tmpArray[ rightEnd ];
}
hs / fub - alp2-12 82
Teile und herrsche: Quicksort
> qSort :: Ord a => [a] -> [a]
> qSort [] = []
> qSort (x:xs) = qSort cmp [y | y <-xs, y <= x]
>
++ [x] ++ qSort cmp [y | y <-xs, y > x ]
Quicksort (typ[ ] a)
1. Pivotelement x wählen
2. Quicksort ( a'[ ]) mit a'[ ] enthält alle y <=x, y aus a[ ]
3. Quicksort (a''[ ]) mit a''[ ] enthält alle y >x
4. a[]sorted = a'''[] mit a'''[i]=a'[i] für i=0…a'.length-1
a'''[k] = x mit k= a'.length
a'''[j] = a''[a'.length+1-j], j=a'.length+1..
a'length+a''length
Beachte elegante funktionale Formulierung !
hs / fub - alp2-12 83
15 3 10 22 7 1 5
3 10 7 1 5
1
3
15
22
5,7,10
5
7,10
10
7
Beachte Wirkung des Pivotelements ⇒
hs / fub - alp2-12 84
15 3 10 22 7 1 5
315
1
3
15 10 22
7
5
15
10
22
Optimales Pivotelement x: Teilung in gleich große Hälften
|
{y: y<=x}| = |{y: y>x}| +/- 1
Überlegung zur Laufzeit: auf jeder Ebene müssen n-1 Elemente
mit dem jeweiligen Pivotelement verglichen werden.
Es gibt log2n Ebenen.
O(n*log n) ist eine gute Vermutung für die Laufzeit im besten Fall,
beweisen! Und im schlechtesten Fall?
hs / fub - alp2-12 85
Idee der Aufteilung des Feldes
15
3
10
22
7
1
5
1
3
10
22
7
15
5
1
3
5
22
7
15
10
Rekursiv fortsetzen
hs / fub - alp2-12 86
Quicksort rekursiv
void qsort(double[] a) {
qsortIdx( a,0, a.length-1);
}
private void qsortIdx(double[] a,int low,int high){
if(low < high) {
int pivotIdx = pivotIdx(a,low,high);
qsortIdx(a, low, pivotIdx-1);
qsortIdx(a,pivotIdx+1,high);
}
}
hs / fub - alp2-12 87
Quicksort: einfache Aufteilung
private int pivotIdx(double[] a, int low, int high){
int l=low,r=high;
double pivot = a[high];
while (l <r) {
while (l <r && a[l] < pivot)
{l++;} // linken Zeiger nach rechts
while (l <r && a[r] >= pivot)
{r--;} // rechten Zeiger nach links
if (l < r)
swapDouble(l,r,a);
}
swapDouble (l, high, a); //pivot -> correct pos
return l;
}
Abhängig von der Wahl des
Pivotelements (!)
hs / fub - alp2-12 88
hs / fub - alp2-12 89
Versuch und Irrtum ( ~ Backtracking)
7
69
4
96
hs / fub - alp2-12 90
7
9
6
6
4
4
6
9 9
9
6 4
hs / fub - alp2-12 91
Problemlösungsmodell
"Backtracking", eine Lösung.
boolean solve (Configuration a){
if(legal(a)) {
nextStep(a);
if (finished(a)) return true;
else for (all continuations b)
if solve(b) return true;
else backtrack(b);
backtrack(a);}
return(false);
}
hs / fub - alp2-12 92
Modell: Tiefensuche
1
2
8
3
7
4
5
6
9
10
12
11
13
Jedem inneren Knoten entspricht ein teilweise gelöstes Problem,
jedem Blattknoten eine Lösung oder eine Sackgasse.
Tiefensuche: löse nacheinander genau die Teilprobleme (1,2,3,4)
die zu einem Blatt führen. Prüfe Lösung. Backtrack! Iteriere
über Alternativen.Alternative
hs / fub - alp2-12 93
n-Damen Problem (n=8)
hs / fub - alp2-12 94
Modellierung des n-Damen-Problems
boolean[][] board = new boolean[N+1][N+1];//use
1..8
legal() = !threat(row,column)
boolean threat(int r, int c){
return vertHorizThreat(r,c) || DiagThread(r,c);
}
finished(column) = return column==N
//ModelL: Alle Damen in unterschiedlichen Zeilen
// und Spalten. Fülle in jedem Schritt eine
// Spalte 1,2,.. ,N
nextstep()= putQueen(row,column);
hs / fub - alp2-12 95
boolean solve (int row, int column){
//1<=row,column<=N
if (threat(row,column)){return false;}
putQueen(row,column);
if (finished(column)) return true;
else{
for (int r=1; r<=N; r++){
if (solve(r,column+1)) return true;
else {backtrack(row, column+1);}
}
backtrack(row, column);
}
return false;
}
hs / fub - alp2-12 96
Suche in einem Labyrinth
1
Modell:
ungerichteter Graph
0
Verzweigungspunkte
des Labyrinths:
Knoten des Graphen,
Verbindungen: Kanten
4
2
3
5
4
1
0
2
3
5
hs / fub - alp2-12 97
Repräsentation von Graphen durch
Adjazenzliste (*)
boolean[][] path
= new boolean[knotenzahl][knotenzahl];
mit path[i][j] = true
0
0
1
2
3
4
5
1
true
true
true true
⇔ Kante zwischen i und j, i ≠ j
2
true
true
3
5
true
true
true
true
4
true
true
true
true
(*) nur eine Möglichkeit
hs / fub - alp2-12 98
Erster Lösungsversuch:
Suche Ausweg (Knoten) {
Gehe Gang bis zum nächsten Knoten k;
Wenn k=Ausgang: fertig
sonst
für alle abzweigenden Gänge suche Ausweg(k);
gehe Gang zurück;
Problem: Kreise!
z.B. 0 → 1 → 2 →0 → 1 → …
Vorsicht: Allgemeinbildung
Faden der Ariadne:
Markiere Knoten, die du schon besucht hast.
hs / fub - alp2-12 99
Suche im Labyrinth
boolean exit[];
boolean mark[] // anfangs false
boolean findPath (int node){
if (marked[node]) return false; //legal?
marked[node]=true;
//next step
//finished?
if (exit[node]) return true;
for (int i= 0; i< graph.length; i++){
if (!graph[node][i]) continue; //all continuat.
if (findPath(i)){
path[next] = i;
// path construction
next++;
Keine besonders effiziente
return true;
Lösung.
}
} // no unmarked successor, no solution.
return false;
}
hs / fub - alp2-12 100
Backtracking wichtiges Lösungsmuster
Verwandt mit "Constraint Satisfaction" Problemen:
Problemlösung unter Nebenbedingungen.
n-Damen-Problem aber nicht Labyrinth!
Anwendungsbeispiele für Backtracking:
• Syntaxanalyse
• Kombinatorische Probleme
• Symbolisches Problemlösen in der KI
(Planungsprozesse, "Suche im Zustandsraum" u.v.m.
oft in Kombination mit Heuristiken)
hs / fub - alp2-12 101
This is the end
Viel Erfolg für die Klausur!
hs / fub - alp2-12 102
Herunterladen