Programm heute Algorithmen und Datenstrukturen (für ET/IT) Sommersemester 0 Dr. Stefanie Demirci Computer Aided Medical Procedures Technische Universität München Einführung Grundlagen von Algorithmen Grundlagen von Datenstrukturen Grundlagen der Korrektheit von Algorithmen Grundlagen der Effizienz von Algorithmen Grundlagen des Algorithmen-Entwurfs Entwurfsprinzipien Divide and Conquer Greedy-Algorithmen Backtracking Dynamisches Programmieren Algorithmen-Entwurf Algorithmen-Muster: Greedy Entwurfsprinzipien: Verfeinerung Algorithmen-Muster Algorithmen-Muster: Divide and Conquer (letzte Stunde) Greedy-Algorithmen Backtracking Dyanmisches Programmieren greedy = gierig, gefräßig Greedy Prinzip: Lösung eines Problems durch schrittweise Erweiterung der Lösung ausgehend von Startlösung in jedem Schritt wähle den bestmöglichen Schritt (ohne Berücksichtigung zukünftiger Schritte) greedy gefundene Lösung muss nicht immer optimal sein!
Algorithmen-Muster: Greedy Greedy: Beispiel Wechselgeld I Greedy-Muster als Pseudocode: Input: Aufgabe A Greedy(A): while (A nicht gelöst) { wähle bestmöglichen Schritt; // Greedy Strategie baue Schritt in Lösung ein; Problem: Herausgabe von Wechselgeld Voraussetzung: übliche Euro-Münzen e, e, 0c, 0c, 0c, c, c und c Aufgabe: Wechselgeld-Herausgabe mit möglichst wenig Münzen Beispiel: Preis e., bezahlt mit e Münze. Wechselgeld: c Minimum Anzahl Münzen: c = 0c +0c +0c +c +c +c Greedy: Beispiel Wechselgeld II Greedy-Algorithmus: Input: Betrag b Wechselgeld(b): ausgegeben = 0; while (ausgegeben < b) { wähle größte Münze s mit ausgegeben+s b; // greedy print(s); ausgegeben += s; Achtung: Abhängig vom Geldsystem liefert dieser Algorithmus nicht immer die optimale Lösung! Beispiel: Münzen c, c, c. Betrag: c Greedy-Lösung: c = c + c + c + c Optimale Lösung: c = c + c Von Greedy lösbare Probleme Voraussetzungen für Anwendbarkeit von Greedy: feste Menge von Eingabewerten Lösungen werden aus Eingabewerten aufgebaut Lösungen lassen sich schrittweise durch Hinzufügen von Eingabewerten aufbauen, beginnend bei leerer Lösung Bewertungsfunktion für partielle und vollständige Lösung Gesucht wird eine/die optimale Lösung
Anwendung Greedy: Glasfasernetz Problemstellung: Aufbau von möglichst billigem Glasfasernetz zwischen n Knoten K,...,K n, so daß alle Knoten miteinander verbunden sind (u.u. mit Umweg) Input: Knoten K,...,K n Glasfasernetz: Beispiel I K 0 K Kosten d ij > 0 für direkte Verbindung zwischen K i und K j für i j, i,j {,...,n K K K K K 0 K K K Output: Teilmenge aller Verbindungen, so daß alle Knoten verbunden sowie minimale Kosten Knoten K,...,K Kosten d ij repräsentiert als gewichteter, ungerichteter Graph (Kapitel ) 0 Glasfasernetz: Beispiel II Glasfasernetz: Beispiel III K K K K K 0 K K K 0 K K Startknoten: K beste Verbindung: zu K, Kosten andere Kosten: (zu K ), 0 (zu K ), (zu K ) nächst-beste Verbindung von {K,K : zu K, Kosten andere Kosten: (zu K ), (zu K ), (zu K )
Glasfasernetz: Beispiel IV Glasfasernetz: Beispiel V K 0 K K K K K 0 K K K K nächst-beste Verbindung von {K,K,K : zu K, Kosten andere Kosten: bzw. (zu K ) nächst-beste Verbindung von {K,K,K,K : zu K, Kosten alle Knoten behandelt, Algorithmus fertig Glasfasernetz: Beispiel VI Glasfasernetz: Algorithmus K 0 K K K K K K K K K Input: Feld von Knoten K (Länge n), Kostenfunktion d(i, j) Output: minimaler Spannbaum B Glasfasernetz(K, d): B = {K ; // Startlösung while (B nicht Spannbaum von {K,...,K n ) { suche billigste Kante, die aus B rausgeht; // Greedy Schritt füge entsprechenden Knoten und Kante zu B hinzu; Ergebnis: ein sog. minimaler Spannbaum (minimum spanning tree, MST) vom Graphen Komplexität naiver Implementation: O(n ) geht besser, s. Kapitel
Programm heute Einführung Grundlagen von Algorithmen Grundlagen von Datenstrukturen Algorithmen-Muster: Backtracking Backtracking: systematische Suchtechnik, um vorgegebenen Lösungsraum vollständig abzuarbeiten Paradebeispiel: Labyrinth. Wie findet Maus den Käse? Grundlagen der Korrektheit von Algorithmen Grundlagen der Effizienz von Algorithmen Grundlagen des Algorithmen-Entwurfs Entwurfsprinzipien Divide and Conquer Greedy-Algorithmen Backtracking Dynamisches Programmieren Backtracking: Labyrinth I Problem: Wie findet Maus den Käse? Lösung: systematisches Abgehen des Labyrinths Zurückgehen falls Sackgasse (daher: Backtracking) trial and error Backtracking: Labyrinth II Mögliche Wege repräsentiert als Baum: (,) (,) (,) (,) (,) (,) (,) (,) (,) 0
Algorithmen-Muster: Backtracking Voraussetzungen: Lösungs(teil)raum repräsentiert als Konfiguration K K 0 ist Start-Konfiguration jede Konfiguration K i kann direkt erweitert werden für jede Konfiguration ist entscheidbar, ob Lösung Backtracking: Konfigurationen (,) (,) Input: Konfiguration K Backtrack(K): if (K ist Lösung) { gib K aus; else { for each (direkte Erweiterung K von K) { Backtrack(K ); initialer Aufruf mittels Backtrack(K 0 ) (,) (,) (,) (,) (,) Konfiguration z.b. repräsentiert als Pfad im Baum (,) (,) Backtracking: Eigenschaften Terminierung von Backtracking: nur wenn Lösungsraum endlich nur wenn sichergestellt daß Konfigurationen nicht wiederholt getestet werden Komplexität von Backtracking: direkt abhängig von Größe des Lösungsraums meist exponentiell, also O( n ), oder schlimmer! nur für kleine Probleme wirklich anwendbar Alternative: Begrenzung der Rekursionstiefe dann Auswahl der bis dahin besten Lösung z.b. für Schach-Programme Backtracking Beispiel: Traveling Salesman Traveling Salesman Problem: n Städte finde kürzeste Rundreise, die alle Städte exakt einmal besucht außer Start- und Zielort (identisch) K 0 K Lösung z.b. mit Algorithmen-Muster Backtracking K K K
Traveling Salesman Problem: Algorithmus mit Backtracking Traveling Salesman Problem: Beispiel K K Input: n Städte, Rundreise trip TSP(trip): if (trip besucht jede Stadt) { erweitere trip um Reise zum Startort; gebe trip und die Kosten aus; else { for each (bislang unbesuchte Stadt s) { trip = trip erweitert um s; TSP(trip ); K 0 bei n Städten mit fixiertem Start-/Zielort gibt es (n )! Rundreisen hier: Städte! = Rundreisen Laufzeit von TSP hier ist O ( (n )! ) hier: kürzeste Rundreise hat Länge K K z.b. über Route K K K K K K Backtracking Beispiel: Acht-Damen-Problem Acht-Damen-Problem Acht-Damen-Problem: suche alle Konfigurationen von Damen auf Schachbrett Zwei der möglichen Lösungen: so daß keine Dame eine andere bedroht A B C D E F G H A B C D E F G H Dame auf Schachbrett: A B C D E F G H A B C D E F G H A B C D E F G H A B C D E F G H Beobachtung: jeweils nur eine Dame pro Zeile/Spalte Lösung z.b. mit Algorithmen-Muster Backtracking
Acht-Damen-Problem: Algorithmus mit Backtracking Acht-Damen-Problem: Illustration Input: Zeilenindex i AchtDamen(i): for h = to { // probiere alle Spalten aus if (Feld in Zeile i, Spalte h nicht bedroht) { setze Dame auf Feld (i,h); if (Brett voll) { // i == gib Lösung aus; AchtDamen(i +); nimm Dame von Feld (i,h) wieder weg; http://www.youtube.com/watch?v=yppfgms 0 Acht-Damen-Problem Programm heute Einführung es gibt Lösungen für das Acht-Damen-Problem das Problem läßt sich auf n Damen auf einem n n Schachbrett ausweiten Anzahl Lösungen wächst stark z.b. für n = gibt es Lösungen ähnliche Spiele, wie z.b. Sudoku, lassen sich entsprechend lösen Grundlagen von Algorithmen Grundlagen von Datenstrukturen Grundlagen der Korrektheit von Algorithmen Grundlagen der Effizienz von Algorithmen Grundlagen des Algorithmen-Entwurfs Entwurfsprinzipien Divide and Conquer Greedy-Algorithmen Backtracking Dynamisches Programmieren
Dynamisches Programmieren Fibonacci Zahlen Dynamisches Programmieren einsetzbar für Probleme, deren optimale Lösung sich aus optimalen Lösungen von Teilproblemen zusammensetzt (z.b. Rekursion) Optimalitätsprinzip von Bellman Prinzip: statt Rekursion berechnet man vom kleinsten Teilproblem aufwärts Zwischenergebnisse werden in Tabellen gespeichert Fibonacci Folge Die Fibonacci Folge ist eine Folge natürlicher Zahlen f,f,f,..., für die gilt f n = f n +f n für n mit Anfangswerten f =, f =. eingesetzt von Leonardo Fibonacci zur Beschreibung von Wachstum einer Kaninchenpopulation Folge lautet:,,,,,,,,,,,... berechenbar z.b. via Rekursion Fibonacci Funktion Input: Index n der Fibonacci Folge Output: Wert f n fib(n): if (n == n == ) { return ; else { return fib(n ) + fib(n ); Beobachtung: Komplexität ist O( n ) fib() fib() fib() fib() gleicher Funktionswert wird mehrfach berechnet! z.b. x fib(), x fib(), x fib() dynamisches Programmieren Aufrufstruktur für fib(): fib() fib() fib() fib() fib() Fibonacci Funktion: dynamisch programmiert Prinzip dynamisches Programmieren: vom kleinsten Teilproblem aufwärts Zwischenergebnisse in Tabelle Input: Index n der Fibonacci Folge Output: Wert f n FibDyn(n): fib = leeres Feld Größe n+; // Tabelle fib[] = ; // kleinstes Teilproblem fib[] = ; // kleinstes Teilproblem for k = to n { fib[k] = fib[k-] + fib[k-]; // Rekursion aufwärts return fib[n]; Komplexität dynamisch programmiert: O(n)
Zusammenfassung Einführung Grundlagen von Algorithmen Grundlagen von Datenstrukturen Grundlagen der Korrektheit von Algorithmen Grundlagen der Effizienz von Algorithmen Grundlagen des Algorithmen-Entwurfs Entwurfsprinzipien Divide and Conquer Greedy-Algorithmen Backtracking Dynamisches Programmieren