Teile und Herrsche

Aus ProgrammingWiki
Archivierte Version vom 2016-01-11T13:28:42+00:00. Aktuelle Version

Inhaltsverzeichnis

Teile und herrsche (Divide and conquer)

Definition

  • Das Entwurfsverfahren Teile und Herrsche dient dem Entwurf rekursiver Algorithmen.
  • Die Idee ist es, ein Problem der Groeßen in mehrere gleichartige aber kleinere Teilprobleme zu zerlegen ( divide).
  • Aus rekursiv gewonnenen Lösungen der Teilprobleme ( conquer) gilt es dann, eine Gesamtlösung für das ursprüngliche Problem zusammenzusetzen .
  • Ziel dieser Methode ist es eine Effiziensverbesserung zu erreichen.


S3anbrem_Anw.PNG

Entwurfsmethode für Algorithmen

1. Teile das Problem in kleinere Unterprobleme (Divide)
2. Löse rekursiv die entstehenden Unterprobleme (Conquer)
3. Setze die Lösungen zusammen.

Eigenschaften

  • kann bei konzeptionell schwierigen Problemen hilfreich sein

  • kann effiziente Lösungen realisieren

  • für Parallelverarbeitung geeignet

Effizienzbetrachtung

Die folgende Rechnung dient als Beweis, dass eine Effizienzverbesserung auftritt:
Wir nehmen an, dass ein bestimmter Algorithmus A ein Problem mit O\left(n^2\right), also T_A\leq c \cdot n^2, löst. Für ein Algorithmus B, der drei dieser Probleme der Größe \frac{n}{2} mit A bearbeitet und mit O\left( n \right) zusammenfügt gilt:

\begin{align} \begin{array}{rcl} T_{B} \left( n \right) & = & 3 \cdot T_{A} \cdot\left( \left\lceil \frac{n}{2} \right\rceil \right) + d \cdot n\\\\

                & \leq & 3 \cdot c \cdot \left( \frac{n+1}{2}\right) ^{2} + d \cdot n\\\\
                & \leq & 3 \cdot c \cdot \left( \frac{n+1}{2}\right) \left( \frac{n+1}{2}\right) + d \cdot n\\\\
                & \leq & 3 \cdot c \cdot \left( \frac{n^{2} + 2n + 1}{4}\right) + d \cdot n\\\\
                & \leq & 3 \cdot c \cdot \left( \frac{n^{2}}{4} + \frac{2n}{4} + \frac{1}{4}\right) + d \cdot n\\\\
                & \leq & \frac{3cn^{2}}{4} + \frac{6cn}{4} + \frac{3c}{4} + d \cdot n\\\\
                & \leq & \frac{3}{4}cn^{2} + \frac{3}{2}cn + \frac{3}{4}c + d \cdot n\\\\
                & \leq & \frac{3}{4}cn^{2} + \left(\frac{3}{2}c + d\right) \cdot n + \frac{3}{4} c\\\\

\end{array} \end{align} Man erkennt an den führenden Summanden, dass sich tatsächlich eine Effizienzsteigerung von ca. 25% ergibt. Allerdings liegt noch immer ein quadratischer Aufwand mit \mathcal{O}\left(n^{2}\right) vor. Daher betrachten wir nun einen Algorithmus \mathcal{C}, welcher das Problem auf rekursive Art löst.

\begin{align} \mathit{T}_{\mathcal{C}}\left(n\right) = \begin{cases} \mathit{T}_{\mathcal{A}}\left(n\right), \mbox{wenn}\ n\leq n_{0} \\ \mathit{3\ T}_{\mathcal{C}}\left(\dfrac{n}{2}\right), \mbox{sonst}\end{cases} \end{align}

Nach dem Lösen dieser Formel kommen wir zu dem Ergebnis \mathit{T}_{\mathcal{C}}\left(n\right)=\mathcal{O}\left(n^{1.59}\right).

Beispiele

Beispiel 1: Die Türme von Hanoi

Die Spielregeln

Es sind n Scheiben unterschiedlichen Durchmessers gegeben, welche geordnet zu einem Turm geschichtet sind, die untere Scheibe ist die größte. Der Turm steht auf einem Anfangsplatz. Unter Verwendung eines Hilfsplatzes soll dieser Turm nun zu einem Zielplatz transportiert werden, wobei verschiedene Regeln einzuhalten sind.

Wenn die Anzahl der Scheiben 1 beträgt, ist die Lösung des Problems trivial.
Bei mehr als einer Scheibe läßt sich das Gesamtproblem in drei Teilprobleme zerlegen:

  • Transportiere n-1 Scheiben vom Anfangsplatz auf den Hilfsplatz.
  • Transportiere die letzte Scheibe vom Anfangsplatz auf Zielplatz.
  • Transportiere n-1 Scheiben vom Hilfsplatz auf den Zielplatz.

S3anbrem_Azh.PNG

Übung 1 : Die Türme von Hanoi


  • Transportiere n Scheiben vom Anfangsplatz auf den Zielplatz, verwende einen Hilfsplatz.


S3anbrem_U1.png

Lösung

S3anbrem_B4.PNG

Spiele

http://www.gf-webdesign.de/tuerme-von-hanoi/tuerme-von-hanoi.htm

Quicksort

Das Verfahren

Quicksort ist ein schneller Sortieralgorithmus auf der Basis rekursiver Vergleiche von Elementen, welcher von C.A.O Hoare im Jahre 1960 entwickelt wurde.

Das Prinzip ist eine gegebene Gesamtliste G so in zwei Teillisten L und R auf zuteilen (divide), dass alle Elemente von L kleiner (oder gleich) als ein Vergleichselement (Pivotelement) v \in G
und alle Elemente von R größer (oder gleich) als v sind.


Es existieren zwei Basisfälle:
1. : G ist leer
2. : G enthält nur ein einziges Element

Wird nun der Quicksortalgorithmus auf jede der enstandenen Teillisten angewandt, ergibt sich dadurch die rekursive Verarbeitung. Und es wird dann nach endlich vielen Zerlegungen einer der beiden Basisfälle erreicht.
Das bedeutet die Teillisten liegen dann sortiert vor (conquer). Letztlich setzt man die einzelnen Teillisten zu der sortierten Gesamtliste Gsortiert zusammmen (combine) und zwar genau in der Form: Gsortiert = Lsortiert v Rsortiert

funktionaler Aufbau



Durch diese Prozedur entstehen die zwei Teillisten L und R, die für die Unterscheidung der Relation in Bezug auf das Pivotelement benötigt werden.

Um nun den Algorithmus richtig anwenden zu können, bedarf es einer weiteren Prozedur, die die Vorhergehende immer wieder rekursiv aufruft und dabei jeweils auf L und R quicksort anwendet um diese zu sortieren.



interne Suchverfahren

Quicksort kann in die Kategorie der internen Suchverfahren eingeordnet werden. Das sind Algorithmen die in-place arbeiten, was bedeutet sie benötigen keinen zusätzlichen Speicherplatz.
Bei der vorherigen Implementierung ergibt sich aber eben dieser Nachteil dadurch, dass die Elemente von G, die die jeweilige Relation (>= v oder <= v) erfüllen, in eine der beiden erzeugten Listen eingetragen werden.
Im Erzeugen von L und R liegt also hier das Problem.

Um die Bedingung des Quicksort Algorithmus (l <= v <= r) erfüllen zu können und den genannten Nachteil zu eliminieren, nutzt man die Idee, die die folgende Implementierung verfolgt. Zunächst um der Speicherplatzbildung entgegenzuwirken muss die Bildung der Teillisten in G erfolgen. Um auf jedes Element zugreifen zu können, wird in der folgenden Implementierung ein Vektor a verwendet.
Das Pivot erhält die erste Poition, also a[0]. Zusätzlich werden zwei Zeiger l und r initiiert, wobei l = a[1] und r = a[n-1].


In Einerschritten wird:

1. l solange nach rechts verschoben bis a[l] > v und
   r solange nach links verschoben bis a[r] < v
2. wenn jeweils ein Element gefunden wurde, wird a[l] mit a[r] getauscht und die Zeigerbewegung fortgesetzt
3. ansonsten wird a[r] mit a[0] vertauscht und a ausgegeben 
4. Insofern die Elemente links bzw. rechts vom Pivotelement aus noch unsortiert sind wiederhole bei 1.

Viualisierung

Die folgende Animation zeigt den verbildlichten Ablauf des Algorithmus:

roter Balken = Pivotelement p der jeweiligen Teilliste
blaue Linie = Wert von p

quicksort_animation.gif

Beispiel 1

Sortieren von (natürlichen) Zahlen:

In diesem Beispiel soll eine Liste L [6,13,3,7,9,7,2,1,15,10,5,6] mit Quicksort sortiert werden.
Es folgt eine ausführliche Lösung zum Erhalt der sortierten Liste:

quicksort_beispiel1a.png
quicksort_beispiel1_2.png

Effizienz von Quicksort

Wegen Gründen der Vereinfachung zwecks Erklärung, wurde im Beispiel 1 das erste Element als Pivotelement gewählt, aber bei Anwendung des Algorithmus sollte man auf die Wahl achten.
Denn wie effizient der Quicksort-Algorithmus ist, hängt in erster Linie von der Wahl des Pivotelementes ab.

Best Case

Im besten Fall wird das Pivotelement so gewählt, dass dieses in der sortierten Liste genau in der Mitte liegt und somit die Gesamtliste G aus (etwa) zwei gleich großen Listen besteht.
Desweiteren arbeitet Quicksort besser je höher die Anzahl der Elemente n ist.

T(0) = 0

T(n) = 2 * T (\frac{n}{2}) + c * n

Nach Auflösung mit der Meistermethode ergibt das einen Aufwand T(n) = \mathcal{O}(n \ \log_{2} \ n) .


Average Case

Im Durchnitt nähert sich die Laufzeit gleichzeitig an den besten bzw. schlechtesten Fall an, was Insgesamt für eine ausgeglichene Laufzeit sorgt.

Worst Case

Im schlechtesten Fall wird das Pivotelement so gewählt, dass dieses in der sortierten Liste entweder am Anfang oder am Ende liegt und somit auf der einen Seite das Pivotelement v und auf der anderen Seite der Rest von G liegt.
Ist außerdem G vor Anwendung von Quicksort bereits sortiert, führt das zu einem sehr langsamen Quicksort, was zum einen aus Definitionssicht wiedersprüchlich erscheint und zum anderen aus Anwendersicht für Konfusion sorgt.


T(0) = 0

T(n) = \underbrace{T(0)}_\text{= 0} + T(n-1) + c * n

Anwendung der Iterationsmethode ergibt:

T(n-1) = T(n-2) + c(n-1)

T(n-2) = T(n-3) + c(n-2)

T\underbrace{( n-k )}_{\text{=1 \curvearrowright k = n - 1}} = T(0) + c(n-k)


Einsetzen aller Ausrücke in darüberstehender Zeile:

T(n) = c * \sum\limits_{k=0}^{n-1} (n-k)

\ \ \ \ \ \ \ \ = c * (\sum\limits_{k=0}^{n-1} (n) - \sum\limits_{k=0}^{n-1} (k))

\ \ \ \ \ \ \ \ = c (n^2 - \frac{n-1}{2}((n-1) + 1))

\ \ \ \ \ \ \ \ = c (\frac{n^2}{2} + \frac{n}{2})

T(n) = \mathcal{O}(n^2)


Anpassung

Die bereits erwähnte Abhängigkeit des Algorithmus von der Wahl des Pivotelementes führt zu den eben berechneten Laufzeiten.
Dabei kann man die Wahl von v durchaus beeinflussen, sodass z.B. die Wahrscheinlichkeit des Auftretens des Worst Cases verringert werden kann.
Außer Acht wird hierbei die Wahl des ersten oder letzten Elementes der Liste gelassen, da dies aus praktischer Sicht unzuverlässig ist.

Möglichkeit 1 v als das mittlerste Element der Liste wählen
Möglichkeit 2v als ein zufällig gewähltes Element setzen
Möglichkeit 3v das erste, das mittlerste und das letzte Element der Liste nehmen und den Median dieser drei bestimmen

Liste L = [10,3,25,6,8,17,1,2,18,5]

Median aus den drei: [10,8,5] -> [5,8,10] ->8

Mergesort

Definition

  • Das Sortier­verfahren Mergesort erzeugt eine sortierte Folge durch Verschmelzen sortierter Teilstücke.
  • Das Verfahren beruht auf dem Divide-and-Conquer-Prinzip Erklärung.
  • Die zu sortierende Folge wird zunächst in zwei Hälften aufgeteilt (divide), die jeweils für sich sortiert werden (conquer).
  • Dann werden die sortierten Hälften zu einer insgesamt sortierten Folge verschmolzen (combine).
  • Dieses Verfahren bietet die Möglichkeit einer iterativen Implementierung.

Vorteile

  • Geschickter Einsatz der Teile-und-Herrsche Technik.
  • Gutes Laufzeitverhalten für große Eingabegrößen
  • Die Stabilität

        Die relative Ordnung zweier Elemente , welche gleich sind , wird beibehalten.

  • Abarbeiten Daten sequentiell.

Nachteile

  • der zusätzlich benötigte Speicher

Anwendung

  • Der Mergesort wird vor allem dann verwendet, wenn man eine große sortierte Datenmenge verwalten möchte.

Effizienzanalysen

Im Gegensatz zur Effizienzanalyse von Quicksort, gibt es bei Mergesort keine Unterscheidung zwischen Worst-, Best- und Average-Case, da hierbei nicht mit einem Pivot-Element gearbeitet wird, sondern "stupide" die Listen immer in der Mitte geteilt werden.Der Algorithmus arbeitet immer gleich, egal ob eine Liste bereits sortiert ist oder nicht.

                Sei \mathit{T}\left(n\right) die Laufzeit von Merge-Sort.
                Das Aufteilen braucht \mathcal{O}\left(1\right) Schritte.
                Die rekursiven Aufrufe brauchen 2\cdot \mathit{T}\left(\frac{n}{2}\right) Schritte.(ein Problem halbiert wird und man dadurch zwei halbe Probleme hat.)
                Das Mischen braucht \mathcal{O}\left(n\right) Schritte.
                Um Mergesort zu analysieren, ist die Rekursionsgleichung : \mathit{T}\left(n\right) = 2\cdot \mathit{T}\left(\frac{n}{2}\right) + \mathcal{O}\left(n\right), wenn n > 1
                Basisfall : \mathit{T}\left(1\right) = 0 , also :

Best-Case: \mathit{T}\left(n\right) = \mathcal{O}\left(n \log n\right)
Average-Case: \mathit{T}\left(n\right) = \mathcal{O}\left(n \log n\right)
Worst-Case: \mathit{T}\left(n\right) = \mathcal{O}\left(n \log n\right)

Viualisierung , Simulation, Animation

S3anbrem_Mergesort-o.gif

http://palash90.in/tools.html#/algorithms/mergeSort http://www.ee.ryerson.ca/~courses/coe428/sorting/mergesort.html

Pseudocode



Beispiele

Beispiel 1

  • Sortieren Sie ein Array.

S3anbrem_Merge-sort-bsp1.PNG

1. Aufteilen der Eingabe

S3anbrem_Merge-sort-bsp1b.PNG

S3anbrem_Merge-sort-bsp1c.PNG

S3anbrem_Merge-sort-bsp1d.PNG

2. Teilen, Sortieren und Mischen der Eingabe

S3anbrem_Merge-sort-bsp1e.PNG

3. Mischen der Eingabe

S3anbrem_Merge-sort-bsp1f.PNG

4. Ergebnis

S3anbrem_Merge-sort-bsp1g.PNG

Beispiel 2

S3anbrem_Anexamplea.png

1. Aufteilen der Eingabe 2. Rekursiv Sortieren 3. Zusammenfügen

S3anbrem_Anexample1.png


Suchalgorithmen

Sequenzielle Suche

Die sequenzielle Such oder auch als Lineare Suche ist die Einfachste aller Suchmethoden. Bei dieser Methode wird von Anfang bis Ende jeder einzelne Eintrag nacheinander mit dem gesuchten Element e verglichen. Sobald ein Listenelement mit e übereinstimmt endet der Algorithmus, sonst wird solange weitergesucht bis das Ende der Liste erreicht ist.

e=11

Kerich_LineareSuche.JPG

Im Worst Case hat der Algorithmus hierbei ein Aufwand T(n)=O(n) , da er jedes Element der Liste durchgehen muss.

Im Best Case einen Aufwand O(1), da bereits das erste Element mit e übereinstimmen kann.

Binäre Suche

Das Binäre Suchen ist ein Teile-und-Herrsche-Verfahren. Dieses Verfahren ähnelt der linearen Suche jedoch benötigt das binäre Suchen eine aufsteigend geordnete Liste.

Ablauf


1. Teile die Liste G in der Mitte sodass 2 gleichgroße Teillisten L=(a_1,a_2,...,a_t), R=(a_{t+1},...,a_x) entstehen.
    Für t gilt t=\frac{x}{2} wobei t ganzzahlig ist. Wenn t nicht ganzzahlig ist wird abgerundet.
2. Vergleiche das erste Element der Teilliste R mit e.

Falls e = n_{t+1}, dann wurde das Element gefunden.
Falls e < n_{t+1}, dann prüfe ob L = null, wenn ja dann wurde das Element e nicht gefunden, sonst gehe zu Schritt 1 und verwende L als neue Liste G.
Falls R = null, dann wurde der Schlüssel nicht gefunden.

Kerich_BinarySearch.PNG

Effizienz

Best-Case


Der Best-Case ist wie beim Die Listen werden immer in der Hälfte geteilt. Somit können immer die Hälfte der Werte ausgeschlossen werden. Geht man dabei vereinfacht von einer Anzahl von n^2 Elementen aus so ist der Aufwand durch die permanente Teilung der Listen T(n)=O(log_2 n). Dies Überdeckt sowohl den Worst-, als auch den Average-Case. Der Best-Case kann hier mit T(n)=O(1) ausgedrückt werden, da er beim ersten Vergleich direkt einen Treffer landen könnte.

Zusammenfassung

  • Teile und Herrsche - Zerlegung größerer Aufgaben oder Probleme in kleinere Aufgaben oder Probleme, die einfacher zu bewältigen oder leichter gelöst werden können.
  • Mergesort - ein schnelleres (und rekursives) Sortierverfahren.
    • Ein Feld wird in zwei Teilfelder aufgeteilt, die dann rekursiv sortiert werden. Anschließend werden diese sortierten Teilfelder wieder zu einem Feld zusammengefügt. Dabei macht man sich zu nutze, daß die beiden Teilfelder bereits sortiert sind.
    • Mit einer Zeit­komplexität von Θ(n log(n)) ist das Verfahren immer optimal.

Übungen

Übung 1 : Hanoi Java-Code

Pseudo - Code

Bedeutung der Variablen:

ap - Anfangsplatz
zp - Zielplatz
hp - Hilfsplatz
n - Anzahl der Scheiben
Die Übergabe des Hilfsplatzes als Parameter kann auch entfallen, wenn man als Berechnung einsetzt:
hp := 6 - ap - zp.

Java-Code

  • Auf der Grundlage diese Pseudocode schreiben Sie Java-Code für Hanoi.
  • Denken Sie auch an der Klasse Main, welche wird die Methode turm benutzen.

Übung 2

  • Sortieren Sie eine Array indem zwei Teilarrays sortiert und dann gemischt werden.

S3anbrem_B1a1.PNG

1. Aufteilen der Eingabe

S3anbrem_B1a.png

2. Rekursiv Sortieren

S3anbrem_B1b.png

3. Zusammenfügen

S3anbrem_B1c.png

Übung 3

Sortieren Sie die Buchstaben in der Zeichenkette aus Ihrem zusammengeschriebenen Vor- und Nachnamen mit Merge Sort.

Übung 4

MergeSort-JavaCode

1. Implementieren Sie eine Klasse „MergeSort".
2. Implementieren Sie in der Klasse eine Methode main .
3. Implementiere Sie eine Variable ms als MergeSort Elemente.
4. Erzeugen Sie ein Array (als generateRandomNumbers-Methode Element) von der Länge , z.B 7 .
5. Drucken Sie Arrays vor und nach der Sortierung.

  • This Methode soll Array sortieren.
  • Benutzen Sie input als Array von Integer .

1. Implementieren middle.
2. Implementiere left als middle.
3. Implementieren Sie die Schleife , welche überprüft ob inputLength ist durch 2 teilbar.


merge

1. Implementieren Sie eine Methode merge ,welche wird zwei integer Arrays mischen , mit dem Parameter left und right .
2. Definieren Sie eindimensionales Array result und initialisieren als Summe Laenge left und right Variablen .
3. Definieren Sie leftIndex, rightIndex und resultIndex und initialisieren mit 0. 4. Implementieren Sie die Schleife while . 5. Die Methode soll am Ande unseres Resultat zurückgeben.


generateRandomNumbers

1. Implementieren Sie eine Methode generateRandomNumbers ,welche wird den Zufallszahlengenerator, mit dem Parameter n(length).
2. Definieren Sie eindimensionales Array und initialisieren als das Array mit n.
3. Benutzen Sie Random .
4. Implementieren Sie die Schleife , welche sucht die Zahlen und speichert diese Zahlen in Array.
5. Die Methode soll am Ande unseres Resultat zurückgeben.


  • http://www.sorting-algorithms.com/

    Quellen

    Wagenknecht, 2003, Algorithmen und Komplexität
    Cormen, Leierson, Rivest und Stein, 2004, Algorithmen - Eine Einführung
    http://www.inf-schule.de/
    http://www.awb1.ch/
    http://www.ki.informatik.uni-frankfurt.de/
    http://ddi.cs.uni-potsdam.de/
    http://thm.de/
    http://de.wikiversity.org/
    http://www.roland-illig.de/