Dynamische Programmierung

Ähnliche Dokumente
Hallo Welt für Fortgeschrittene

Dynamische Programmierung. Problemlösungsstrategie der Informatik

Dynamische Programmierung

Einführung in die Objektorientierte Programmierung Vorlesung 17: Dynamische Programmierung. Sebastian Küpper

Algorithmen und Datenstrukturen 2

Top-down Bottom-up Divide & Conquer Dynamisches Programmieren Caching (Memoization) Branch-and-Bound Greedy

Algorithmen und Datenstrukturen 1 Kapitel 3

Dynamische Programmierung

Dynamische Programmierung

Datenstrukturen und Algorithmen

Lösungsvorschlag Serie 2 Rekursion

Algorithmen und Komplexität

Ein Dieb raubt einen Laden aus; um möglichst flexibel zu sein, hat er für die Beute nur einen Rucksack dabei

9. Rekursion. 1 falls n 1 n (n 1)!, andernfalls. Experiment: Die Türme von Hanoi. Links Mitte Rechts. Mathematische Rekursion

Datenstrukturen & Algorithmen

Rekursive Funktionen

19. Dynamic Programming I

Dynamische Programmierung

Dynamisches Programmieren - Problemstruktur

Wiederholung. Divide & Conquer Strategie

12. Rekursion Grundlagen der Programmierung 1 (Java)

Technische Universität München. Kombinatorik. Christian Fuchs

Dynamische Programmierung II

Rekursive Funktionen

4 Rekursionen. 4.1 Erstes Beispiel

Einstieg in die Informatik mit Java

Konzepte der Informatik

Hallo Welt für Fortgeschrittene

Algorithmen und Datenstrukturen (ESE) Entwurf, Analyse und Umsetzung von Algorithmen (IEMS) WS 2014 / Vorlesung 9, Donnerstag 18.

6. Algorithmen auf Zeichenketten

Algorithmen und Datenstrukturen Tafelübung 4. Jens Wetzl 15. November 2011

Schnittstellen, Stack und Queue

19. Dynamic Programming I

3.3 Optimale binäre Suchbäume

Rekursive Funktionen und ihre programmtechnische Umsetzung

Speicher und Adressraum

Algorithmen und Datenstrukturen

Lösungen von Übungsblatt 12

1 Einführung. 2 Grundlagen von Algorithmen. 3 Grundlagen von Datenstrukturen. 4 Grundlagen der Korrektheit von Algorithmen

Erste Java-Programme (Scopes und Rekursion)

Komplexität von Algorithmen

Programmieren 1 C Überblick

Stand der Vorlesung Komplexität von Algorithmen (Kapitel 3)

Algorithmen und Datenstrukturen 2

Vorkurs Informatik WiSe 15/16

Algorithmen und Datenstrukturen 2

13 Java 4 - Entwurfsmuster am Beispiel des Rucksackproblems

21. Greedy Algorithmen. Aktivitätenauswahl, Fractional Knapsack Problem, Huffman Coding Cormen et al, Kap. 16.1, 16.3

Algorithmen und Datenstrukturen (für ET/IT)

Algorithmen & Datenstrukturen Midterm Test 2

Programmierung 2. Dynamische Programmierung. Sebastian Hack. Klaas Boesche. Sommersemester

Entscheidungsbäume. Definition Entscheidungsbaum. Frage: Gibt es einen Sortieralgorithmus mit o(n log n) Vergleichen?

( )= c+t(n-1) n>1. Stand der Vorlesung Komplexität von Algorithmen (Kapitel 3)

Stud.-Nummer: Datenstrukturen & Algorithmen Seite 1

Übungspaket 22 Rekursive Funktionsaufrufe

Technische Informatik I - HS 18

Rekursion. Annabelle Klarl. Einführung in die Informatik Programmierung und Softwareentwicklung

19. Dynamic Programming I

Algorithmen und Datenstrukturen 2 VU 3.0 Nachtragstest SS Oktober 2016

Algorithmen I - Tutorium 28 Nr. 12

public static void main(string[] args) {

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Theoretische Informatik. Exkurs: Komplexität von Optimierungsproblemen. Optimierungsprobleme. Optimierungsprobleme. Exkurs Optimierungsprobleme

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Einfache Arrays. Dr. Philipp Wendler. Zentralübung zur Vorlesung Einführung in die Informatik: Programmierung und Softwareentwicklung

Stack. Seniorenseminar Michael Pohlig

Datenstrukturen und Algorithmen

Programmierung für Mathematik (HS13)

Übung zu Algorithmen und Datenstrukturen (für ET/IT)

11. Rekursion, Komplexität von Algorithmen

II.3.1 Rekursive Algorithmen - 1 -

1. Teilklausur Gruppe A. Bitte in Druckschrift leserlich ausfüllen!

Vorkurs Informatik WiSe 17/18

6 Speicherorganisation

Schwerpunkte. Verkettete Listen. Verkettete Listen: 7. Verkettete Strukturen: Listen. Überblick und Grundprinzip. Vergleich: Arrays verkettete Listen

Transkript:

Dynamische Programmierung Hannes Schwarz - WS-06/07 Hannes.Schwarz@uni-konstanz.de Getting Ready for the ACM Programming Contest

Übersicht Übersicht Was ist dynamische Programmierung? Entwicklung eines Algorithmus Beispiel: Fibonacci-Folge Implementierung Memoization & Teilprobleme

Übersicht Rucksackproblem Implementierung Top-down vs. Bottom-up Binomialkoeffizienten Fazit Quellen

Was ist dynamische Programmierung? Definition: Algorithmen der dynamischen Programmierung kommen bei Optimierungsproblemen zur Anwendung, bei denen das Endergebnis aus einer unbekannten Kombination von vielen, nicht notwendigerweise unabhängigen Teilergebnissen berechnet werden kann. Quelle: http: //dynamische_programmierung.know-library.net

Was ist dynamische Programmierung? Einsatzgebiet: Optimierungsprobleme Endergebnis ist Kombination aus Teilergebnissen Teilergebnisse nicht zwingend unabhängig Kombination unbekannt

Woher kommt dynamische Programmierung? Teile & Herrsche ist Grundlage Geschickt implementierte Rekursion Zwischenergebnisse in Tabelle speichern Typischer Einsatz: Suche nach minimaler oder maximaler Kombination

Woher kommt dynamische Programmierung? Begriff in den 1940er Jahren von Richard Bellman geprägt Mathematisches Verfahren Programmierung bezieht sich auf das Ausfüllen der Tabelle

Entwicklung eines Algorithmus mit dynamischer Programmierung Voraussetzung: Die optimale Lösung setzt sich aus optimalen Teillösungen zusammen. 1. Charakterisierung der Struktur 2. Rekursive Definition des Wertes einer optimalen Lösung 3. Berechnung des Wertes einer optimalen Lösung (rekursiv) 4. Konstruktion der optimalen Lösung aus berechneter Information

Fibonacci-Folge Definition: f(1) = f(2) = 1 f(n) = f(n 2) + f(n 1) n > 2 Rekursiv definiert rekursive Implementierung?

Fibonacci-Folge - rekursive Implementierung static int knownf[] = new int[1000]; int fib(int n) { if( knownf[n]!= 0) { return knownf[n];} if( n == 1 n == 2) { return 1; } int newfib = fib(n-2) + fib(n-1); return newfib; }

Fibonacci-Folge - rekursive Implementierung Rekursionsbaum für f(7) exponentieller Aufwand O(2 n )

Fibonacci-Folge - rekursive Implementierung mit Köpfchen static int knownf[] = new int[1000]; int fib(int n) { if( knownf[n]!= 0) { return knownf[n];} if( n == 1 n == 2) { return 1; } int newfib = fib(n-2) + fib(n-1); return newfib; }

Fibonacci-Folge - rekursive Implementierung mit Köpfchen static int knownf[] = new int[1000]; int fib(int n) { if( knownf[n]!= 0) { return knownf[n];} if( n < 2) { return 1; } int newfib = fib(n-2) + fib(n-1); return newfib; }

Fibonacci-Folge - rekursive Implementierung mit Köpfchen static int knownf[] = new int[1000]; int fib(int n) { if( knownf[n]!= 0) { return knownf[n];} if( n < 2) { return 1; } int newfib = fib(n-2) + fib(n-1); return knownf[n] = newfib; }

Fibonacci-Folge - rekursive Implementierung mit Köpfchen Weiterhin Rekursion Rekursionsbaum für f(7) Ergebnisse werden gespeichert Zuerst lookup in Tabelle linearer Aufwand O(n)

Fibonacci-Folge Laufzeiten für fib(50) = 12586269025 Algorithmus Zeit rekursiv 13 min. 12.sek. iterativ 0.004 sek. rek. + Köpfchen 0.004 sek.

Optimale Teilprobleme Zerlegung wie bei Teile&Herrsche ABER: Teilprobleme müssen nicht unabhängig sein Zerlegung liefert Rekursionsgleichung für das Problem Teilprobleme sind optimal Basisfälle aufstellen (n=1)

Überlappende Teilprobleme Rekursion löst immer gleiche Teilprobleme überlappende Teilprobleme Teile&Herrsche : unterschiedliche Teilprobleme Einsatz von Memoization Dynamische Programmierung ist nichts weiter als das Lösen von Teilproblemen, wobei die Mehrfachberechnung von Teilproblemen aufgrund von Überschneidungen durch Memoization erschlagen wird.

Memoization Idee: ineffiziente Rekursion wird durch Füllen der Tabelle effizient Tabelle ein- oder mehrdimensionales Array Programm sieht wie folgt aus: Wert schon berechnet? zurückliefern Wert noch nicht berechnet? berechnen, speichern, zurückliefern

Rucksackproblem? 17 kg

Rucksackproblem Rucksack der Größe c N Typen von Gegenständen Jeder Typ n hat einen Wert vn sowie einen Platzbedarf von sn Jetzt packen wird den Rucksack: Die Kapazität c nicht überschritten wird Der Gesamtwert maximal ist

Rucksackproblem - Entwurf der optimalen Lösung Liste von k Gegenstandstypen n: L c = (n 1, n 2,..., n k ) Maximaler Wert des Rucksack ist die Summe aller Werte seiner Gegenstände w c = k i=1 v i

Rucksackproblem - Aufstellen der Rekursionsgleichung Lc ist für die Kapazität c eine optimale Lösung Wenn wir ein Element aus Lc entfernen ist die Restliste eine optimale Lösung für die Kapazität c - snk Bei jedem Schritt ist eine Auswahl zu treffen: füge weiteres Element hinzu, so dass Die Kapazität nach wie vor unter der Gesamtkapazität liegt Der erhöhte Gesamtwert maximal ist

Rucksackproblem - rekursive Lösung Geg.: static class Item{int size; int val;} Item items[]; static int knap(int cap) { int i, space, max, t; for(i=0, max=0; i<items.length;i++) if((space = cap-items[i].size) >=0) if((t=knap(space) + items[i].val)>max) max=t; return max; }

Rucksackproblem - rekursive Lösung Geg.: static class Item{int size; int val;} Item items[]; FINGER WEG!!!! static int knap(int cap) { int i, space, max, t; for(i=0, max=0; i<items.length;i++) if((space = cap-items[i].size) >=0) if((t=knap(space) + items[i].val)>max) max=t; return max; }

Rucksackproblem - rekursive Lösung mit Memoization static int knap(int cap) { int i, space, max, t; if(maxknown[cap]!= unknown) return maxknown[cap]; for(i=0, max=0; i<items.length;i++) if((space = cap-items[i].size) >=0) if((t=knap(space) + items[i].cal)>max) max=t; return max; }

Rucksackproblem - rekursive Lösung mit Memoization static int knap(int cap) { int i, space, max, t; if(maxknown[cap]!= unknown) return manknown[cap]; for(i=0, max=0; i<items.length;i++) if((space = cap-items[i].size) >=0) if((t=knap(space) + items[i].val)>max) max=t; maxknown[cap] = max; return max; }

Rucksackproblem - rekursive Lösung mit Memoization Laufzeit ohne Memoziation c Zeit 100 0.9sec 125 51sec 135 4min 28sec 150 52min 48sec Laufzeit mit Memoziation c Zeit 1.000 5ms 100.000 20ms 500.000 80ms 1.000.000 145ms von exponentieller zu linearer Laufzeit

Rucksackproblem - Rekonstruktion des Lösungswegs Bisheriges Ergebnis: Wert unseres maximal gepackten Rucksacks Welche Gegenstände sind eigentlich im Rucksack?

Rucksackproblem - Rekonstruktion des Lösungswegs In jedem Schritt Gegenstand merken - weiterer Array Für jeden Kapazitätswert einen eindeutigen Gegenstand Dieser Gegenstand ist für die Größe optimal und daher fest

Rucksackproblem - Rekonstruktion des Lösungswegs Rekonstruktion beginnt bei der Gesamtkapazität c Der Index des ersten Gegenstands befindet sich in itemknown[c] Wurde der Gegenstand aufgenommen c = c -itemknown[c].size Nächster Index also itemknown[c -itemknown[c].size] usw.

Rucksackproblem - die endgültige Lösung static int knap(int cap) { int i, space, max, maxi, t; if(maxknown[cap]!= unknown) return manknown[cap]; for(i=0, max=0; i<n;i++) if((space = cap-items[i].size) >=0) if((t=knap(space) + items[i].val)>max) max=t; maxi = i; maxknown[cap] = max; itemknown[cap] = items[maxi]; return max; }

Top-down Rekursive Berechnung ist ein Top-down -Ansatz Bisher Effizienzgewinn durch Memoization Probleme durch: Hohe Rekursionstiefen (Stack Overflow) Overhead durch Funktionsaufrufe

Bottom-up Zuerst Tabelle mit Teillösungen füllen Lösung schliesslich aus Tabelle auslesen Performanter als Top-down -Ansatz Bottom-up -Ansatz schwerer zu finden

Bottom-up vs. Top-down Bottom-up -Ansatz Zuerst wird eine Optimallösung von Teilproblemen bestimmt Danach Auswahl der optimalen Teillösungen zu Konstruktion einer Lösung des ursprünglichen Problems Top-down -Ansatz Zuerst Auswahl einer vielversprechenden Alternative Danach Lösung der resultierenden Teilprobleme

Binomialkoeffizienten Problem: Anzahl Möglichkeiten k (numerierte) Studenten aus einem Seminarraum mit n Personen zu wählen und zum World Final zu schicken Lösung: ( ) n k = n! (n k)!k!

Binomialkoeffizienten Rekursionsgleichung ( ) n k = ( ) n 1 k 1 + ( ) n 1 k Das Pascalsche Dreieck n = 0 1 n = 1 1 1 n = 2 1 2 1 n = 3 1 3 3 1 n = 4 1 4 6 4 1 n = 5 1 5 10 10 5 1 n = 6 1 6 15 20 15 6 1

Binomialkoeffizienten - Die Bottom-up -Lösung Zur Lösung Pascalsche Dreieck Array[n][n+1] Die Ränder [0] [0] bis [0] [n] und [0] [0] bis [n] [n] werden mit Einsen gefüllt Zur Berechnung wird auf vorherige Ergebnisse zurückgegriffen An Position [n] [m] steht der gesuchte Wert

Binomialkoeffizienten - Die Bottom-up -Lösung static void bin() { int dreieck [][] = new int [7][]; for ( int i = 0; i < dreieck.length; i++ ) { dreieck[i] = new int[i+1]; for ( int j = 0; j <= i; j++ ) { if ( (j == 0) (j == i) ) dreieck[i][j] = 1; else dreieck[i][j]=dreieck[i-1][j-1]+dreieck[i-1][j]; System.out.print( dreieck [i][j] + " " ); } System.out.println(); } }

Binomialkoeffizienten - Die Bottom-up -Lösung Kein Overhead durch Funtionsaufrufe Aber: zuviele Werte berechnet Für den Wert xi,j sind xi-1,j-1 und xi-1,j Werte relevant Zur Berechnung von benötigt ( ) n m werden also keine xi,j mit j > m Ausfüllen der Tabelle in jeder Zeile zum m-ten Wert ausreichend

Fibonacci-Folge - rekursive Implementierung mit Memoization static int knownf[] = new int[1000]; int fib(int n) { if( knownf[n]!= 0) { return knownf[n];} if( n < 2) { return 1; } int newfib = fib(n-2) + fib(n-1); return knownf[n] = newfib; }

Fibonacci-Folge - iterative Implementierung (Bottom-up) static int iterativ(int n) { int[] ita = new int[n+1]; ita[1] = 1; ita[2] = 1; for(int i = 3; i < ita.length; i++ ) { ita[i] = ita[i-2] + ita [i-1]; } return ita[n]; }

Fazit Suche nach Optimum Optimales Ergebnis lässt sich in optimale Teilergebnisse aufteilen Jeder Schritt zum Ziel benötigt eine Auswahl Überlappungen können auftreten

Fazit Nicht immer möglich die Lösung kleinerer Probleme so zu kombinieren, dass das große Problem gelöst ist Teilprobleme können unvertretbar groß sein Nicht genau angegeben welche Probleme DP effizient löst

Weitere Einsatzgebiete von dynamischer Programmierung Bioinformatik Sequenzierung von Genen und Proteinen Ähnliche Problemstellungen wie bei Stringvergleichen Linguistik CYK -Algorithmus

Literaturempfehlungen R. Sedgewick; Algorithmen in Java; Pearson, 2003 S. Skiena; The Algorithm Design Manual; Springer, 1998 V. Heun; Grundlegende Alhorithmen; Vieweg, 2003

Internetquellen http://de.wikipedia.org/wiki/dynamische_programmierung http://www.informatik.uni-leipzig.de/lehre/heyer0001/ad2-vorl2/index.htm http://ddi.uni-muenster.de/personen/marco/dynprogrammieren.pdf http://www2.informatik.uni-erlangen.de/lehre/ss2006/hallowelt/ dynamische_programmierung.pdf?language=en http://www2.informatik.uni-erlangen.de/lehre/ss2005/hallowelt/ dyn_programming.beamer.pdf?language=de

Vielen Dank für die Aufmerksamkeit!