13 Java 4 - Entwurfsmuster am Beispiel des Rucksackproblems 13.1 Modellierung des Rucksackproblems 13.2 Lösung mit Greedy-Algorithmus 13.3 Lösung mit Backtracking 13.4 Lösung mit Dynamischer Programmierung 13.5 Lösung mit Genetischem Algorithmus 234
Teil XIII Java 4 - Entwurfsmuster am Beispiel des Rucksackproblems Überblick Modellierung des Rucksackproblems 13 Lösung mit Greedy-Algorithmus Lösung mit Backtracking Lösung mit Dynamischer Programmierung Lösung mit Genetischem Algorithmus Saake/Schallehn Algorithmen & Datenstrukturen 13 1 Wiederholung: Rucksackproblem Einfaches Optimierungsproblem Gegeben: n Gegenstände mit Gewichten g 1 bis g n und Nutzwerten w 1 bis w n Rucksack mit maximaler Kapazität C Gesucht: Auswahl von k {1..n Gegenständen, so dass die Maximalkapazität nicht überschritten wird Σ i k g i C und der Gesamtnutzen maximal ist: Σ i k w i max Saake/Schallehn Algorithmen & Datenstrukturen 13 2 Uni Magdeburg, WS 2005/06 235
Datenstruktur statische Variablen für Problembeschreibung Gewicht und Nutzen der Objekte in einem zweidimensionalen Array der Größe anzahlobjekte 2 public class Rucksack { static int anzahlobjekte = 10; static int[][] auswahlobjekte = null;... Gewichte und Nutzwerte werden zufällig zwischen 1 und jeweiligem Maximalwert generiert static final int MAX_GEWICHT = 10; static final int MAX_NUTZEN = 10; Saake/Schallehn Algorithmen & Datenstrukturen 13 3 Generierung der Auswahlobjekte static int[][] erzeugeobjekte() { java.util.random ra = new java.util.random(); int[][] r = new int[anzahlobjekte][2]; for (int i=0; i < r.length; i++) { r[i][0]= ra.nextint(max_gewicht)+1; r[i][1]= ra.nextint(max_nutzen)+1; return r; Saake/Schallehn Algorithmen & Datenstrukturen 13 4 Auswahlobjekte anzahlobjekte kann als Kommandozeilenparameter übergeben werden, ansonsten Default-Wert 10 auswahlobjekte[i][0] enthält Gewichte auswahlobjekte[i][1] enthält Nutzen Indexiläuft von0bisanzahlobjekte-1 Beispiel füranzahlobjekte = 6: AuswahlObjekte Gewicht: Nutzen: 7 5 8 3 3 2 1 5 2 2 1 9 Saake/Schallehn Algorithmen & Datenstrukturen 13 5 236 Uni Magdeburg, WS 2005/06
Kapazität der Rucksäcke Kapazität als weitere statische Variable static int kapazitaet; Initialisierung in dermain-methode kapazitaet = (int) (anzahlobjekte * MAX_GEWICHT / 4); dadurch passen im Schnitt nur die Hälfte der Gegenstände in den Rucksack Saake/Schallehn Algorithmen & Datenstrukturen 13 6 Rucksäcke Implementierung eines Rucksacks als Auswahl der vorgegebenen Objekte einzige nicht-statische Variable 13 boolean[] auswahl = null; Beispiel: Konstruktor zum Erzeugen eines leeren Rucksacks Rucksack () { auswahl = new boolean[anzahlobjekte]; for (int i = 0; i < auswahl.length; i ++) auswahl[i] = false; Saake/Schallehn Algorithmen & Datenstrukturen 13 7 Rucksäcke /2 Copy-Konstruktor zum Erzeugen einer Kopie eines existierenden Rucksacks tostring()-methode zur Ausgabe eines Rucksacks Methoden zum Berechnen des Gesamtgewichts und des Gesamtnutzens, z.b. int gewicht () { int g = 0; for (int i=0; i < auswahl.length; i ++) if (auswahl[i] == true) g = g + auswahlobjekte[i][0]; return g; Saake/Schallehn Algorithmen & Datenstrukturen 13 8 Uni Magdeburg, WS 2005/06 237
Rucksäcke /3 AuswahlObjekte Gewicht: Nutzen: Rucksack 1: Rucksack 2: 7 5 8 3 3 2 1 5 2 2 1 9 T F T T F F Beinhaltet Gegenstände 0, 2 und 3 Gesamtgewicht: 18 Gesamtnutzen: 5 F T T F F T Beinhaltet Gegenstände 1, 2 und 5 Gesamtgewicht: 15 Gesamtnutzen: 16 Saake/Schallehn Algorithmen & Datenstrukturen 13 9 Greedy-Algorithmus Greedy-Grundprinzip: verwende in jedem Berechnungsschritt die jeweils aktuell geeignetste Zwischenlösung Angewandt auf Rucksackproblem: lege von den noch nicht im Rucksack befindlichen Gegenständen jeweils den besten hinzu Was ist der beste Gegenstand? der nützlichste? der leichteste? der mit dem besten Verhältnis aus Nutzen und Gewicht?... Saake/Schallehn Algorithmen & Datenstrukturen 13 10 Greedy nach Nutzen static Rucksack packegierignachnutzen() { Rucksack r = new Rucksack(); while (true) { int pos=-1; int besternutzen=0; for(int i=0; i < auswahlobjekte.length; i ++) if (r.auswahl[i] == false && auswahlobjekte[i][1] > besternutzen && r.gewicht() + auswahlobjekte[i][0] <= kapazitaet) { besternutzen = auswahlobjekte[i][1]; pos = i; if (pos == -1) break; else r.auswahl[pos] = true; return r; Saake/Schallehn Algorithmen & Datenstrukturen 13 11 238 Uni Magdeburg, WS 2005/06
Greedy nach Gewicht static Rucksack packegierignachgewicht() { Rucksack r = new Rucksack(); while (true) { int pos=-1; int bestesgewicht=max_gewicht+1; for(int i=0; i < auswahlobjekte.length; i ++) if (r.auswahl[i] == false && auswahlobjekte[i][0] < bestesgewicht && r.gewicht() + auswahlobjekte[i][0] <= kapazitaet) { bestesgewicht = auswahlobjekte[i][0]; pos = i; if (pos == -1) break; else r.auswahl[pos] = true; return r; Saake/Schallehn Algorithmen & Datenstrukturen 13 12 Aufruf inmain() public static void main (String args[]) { if (args.length == 1) anzahlobjekte = Integer.parseInt(args[0]); kapazitaet = (int) (anzahlobjekte * MAX_GEWICHT / 4); auswahlobjekte = erzeugeobjekte(); 13 Rucksack r1 = packegierignachgewicht(); System.out.println( Greedy Gewicht: + r1 ); Rucksack r2 = packegierignachnutzen(); System.out.println( Greedy Nutzen: + r2 );... Saake/Schallehn Algorithmen & Datenstrukturen 13 13 Analyse der Greedy-Varianten Vorteil: Geringer Berechnungsaufwand durch lineare Komplexität O(n) Problem: Findet im allgemeinen nicht die optimale Lösung Saake/Schallehn Algorithmen & Datenstrukturen 13 14 Uni Magdeburg, WS 2005/06 239
Backtracking-Verfahren Grundprinzip: Finden der optimalen Lösung durch systematisches Absuchen des gesamten Lösungsraums Angewandt auf Rucksackproblem: 2 n verschiedene Möglichkeiten Generieren und Testen aller möglichen Rucksäcke Anwendung der Rekursion Saake/Schallehn Algorithmen & Datenstrukturen 13 15 Backtracking: Rekursionseinstieg static Rucksack packeoptimalmitbacktracking() { return rucksackrekursiv(0, new Rucksack()); erster Parameter: Level i - Entscheidung, ob Objekt i in den Rucksack kommt Durchlaufen desauswahl-arrays von links nach rechts Aufrufgraph: Aufspannen eines binären Baumes durch ja/nein-entscheidungen Saake/Schallehn Algorithmen & Datenstrukturen 13 16 Backtracking: Rekursion static Rucksack rucksackrekursiv(int i, Rucksack r) { if (i == auswahlobjekte.length) return r; Rucksack r1 = new Rucksack(r); r1 = rucksackrekursiv(i+1, r1); if (r.gewicht()+auswahlobjekte[i][0]<=kapazitaet) { Rucksack r2 = new Rucksack(r); r2.auswahl[i] = true; r2 = rucksackrekursiv(i+1,r2); if (r2.nutzen() > r1.nutzen()) return r2; return r1; Saake/Schallehn Algorithmen & Datenstrukturen 13 17 240 Uni Magdeburg, WS 2005/06
Analyse der Backtracking-Varianten Problem: Extrem hoher Berechnungsaufwand für große Auswahl an Objekten durch Komplexität O(2 n ) Vorteil: Findet garantiert optimale Lösung Saake/Schallehn Algorithmen & Datenstrukturen 13 18 Dynamische Programmierung Grundprinzip: Wiederverwendung von bereits berechneten Teillösungen Vorsicht: andere Lösung als im vorhergehenden Kapitel, wo Teillösungen Bottom-Up zusammengesetzt wurden Hier: Lösung basiert auf Backtracking-Variante Zwischenspeicherung von Teillösungen Existenz von Teillösung wird als Abbruchkriterium für Rekursion verwendet 13 Saake/Schallehn Algorithmen & Datenstrukturen 13 19 DP: Rekursionseinstieg static Rucksack packemitdynamischerprogrammierung() { Rucksack[][] zwischenergebnisse = new Rucksack[kapazitaet+1][anzahlObjekte]; return rucksackrekursivdp(0, new Rucksack(), zwischenergebnisse); ein EintragzwischenErgebnisse[g][i] bedeutet, dass wir schonmal dabei waren, das Objekt i in einen Rucksack mit dem Gewicht g zu legen in diesem Fall können wir alle vorberechneten Entscheidungen für die Objekte i bis anzahlobjekte 1 wiederverwenden, da diese bereits optimal sind (Backtracking: äquivalenter Teilbaum) Saake/Schallehn Algorithmen & Datenstrukturen 13 20 Uni Magdeburg, WS 2005/06 241
DP: Rekursion static Rucksack rucksackrekursivdp(int i, Rucksack r, Rucksack[][] zwischenergebnisse) { if (i == auswahlobjekte.length) return r; int gewicht = r.gewicht(); Wiederverwendung von Teillösungen: if (zwischenergebnisse[gewicht][i]!= null) { for (int j=i; j < anzahlobjekte; j++) r.auswahl[j] = zwischenergebnisse[gewicht][i].auswahl[j]; return r;... Saake/Schallehn Algorithmen & Datenstrukturen 13 21 DP: Rekursion /2... Rucksack r1 = new Rucksack(r); r1 = rucksackrekursivdp(i+1, r1, zwischenergebnisse); if (gewicht+auswahlobjekte[i][0] <= kapazitaet) { Rucksack r2 = new Rucksack(r); r2.auswahl[i] = true; r2 = rucksackrekursivdp(i+1,r2, zwischenergebnisse); if (r2.nutzen() > r1.nutzen()) r1 = r2; Merken von Teillösungen: zwischenergebnisse[gewicht][i] = r1; return r1; Saake/Schallehn Algorithmen & Datenstrukturen 13 22 Analyse der DP-Variante Vorteile: Findet garantiert optimale Lösung In vielen Fällen geringerer Aufwand als Backtracking Problem: Anwendbarkeit/Aufwand abhängig von Größe und Struktur des Suchraums Komplexität O(z), wobei z Anzahl möglicher Zwischenergebnisse zum Beispiel: bei vielen unterschiedlichen Gewichtskombinationen kaum Ersparnis (Erhöhen von MAX_GEWICHT) bei überabzählbarem Suchraum (etwa Gewicht als rationale Zahl) macht das Verfahren keinen Sinn Saake/Schallehn Algorithmen & Datenstrukturen 13 23 242 Uni Magdeburg, WS 2005/06
Genetische Algorithmen Grundprinzipien: 1. Erzeugen von zufälligen Lösungen durch Mutation und Kreuzung 2. Auswahl der besten Lösungen aus dem Gen-Pool nach Fitness-Funktion 3. Fortfahren mit Schritt 1 über bestimmte Anzahl von Generationen Angewandt auf Rucksackproblem: Erzeugen von zufälligen Rucksäcken durch Mutation: zufälliges Umschalten von Auswahlbits Punktkreuzung: Kopieren von Auswahlbits Fitnessfunktion: 0, wenn Rucksack zu schwer Gesamtnutzen, andernfalls Parameter: Poolgröße, Auswahlgröße, Mutationsrate, Generationenzahl,... Saake/Schallehn Algorithmen & Datenstrukturen 13 24 Fitness Fitness, Mutation und Kreuzung als Objektmethoden (anwendbar auf einzelne Rucksäcke) 13 int fitness() { if (gewicht() > kapazitaet) return 0; else return nutzen(); Saake/Schallehn Algorithmen & Datenstrukturen 13 25 Mutation Rucksack mutate() { java.util.random ra = new java.util.random(); int pos = ra.nextint(auswahl.length); Rucksack r = new Rucksack(this); r.auswahl[pos] = (auswahl[pos]? false : true); return r; Saake/Schallehn Algorithmen & Datenstrukturen 13 26 Uni Magdeburg, WS 2005/06 243
Kreuzung Rucksack crossover(rucksack partner) { java.util.random ra = new java.util.random(); int pos = ra.nextint(auswahl.length); Rucksack r = new Rucksack(); for (int i=0; i < pos; i++) r.auswahl[i] = auswahl[i]; for (int i=pos; i < auswahl.length; i++) r.auswahl[i] = partner.auswahl[i]; return r; Saake/Schallehn Algorithmen & Datenstrukturen 13 27 Genetischer Algorithmus Erzeugung und Initialiserung des Gen-Pools static Rucksack packemitgenetik() { int POOL_SIZE=50; int BEST_SIZE=10; Rucksack[] pool = new Rucksack[POOL_SIZE]; pool[0] = new Rucksack(); for (int i=1; i < BEST_SIZE; i++) pool[i]... = pool[0].mutate(); Saake/Schallehn Algorithmen & Datenstrukturen 13 28 Aufbau des Gen-Pools Gen-Pool 0 1.. BEST_SIZE-1... POOL_SIZE-1 Saake/Schallehn Algorithmen & Datenstrukturen 13 29 244 Uni Magdeburg, WS 2005/06
Genetischer Algorithmus /2 Iterativ Mutieren und Kreuzen... int NUM_GENERATIONS=anzahlObjekte*10; int generation = 0; while(generation < NUM_GENERATIONS) { for (int i = BEST_SIZE; i < POOL_SIZE; i++) { java.util.random ra = new java.util.random(); if (ra.nextfloat() < 0.7) pool[i] = pool[i%best_size].mutate(); else pool[i] = pool[i%best_size].crossover( pool[ra.nextint(best_size)]);... Saake/Schallehn Algorithmen & Datenstrukturen 13 30 Mutation und Kreuzung Gen-Pool 13 Mutation und Kreuzung 30% K 70% M Saake/Schallehn Algorithmen & Datenstrukturen 13 31 Genetischer Algorithmus /3 Auswahl der besten Kandidaten jeder Generation... for (int i = BEST_SIZE; i < POOL_SIZE; i ++) { int worstfitness = Integer.MAX_VALUE; int pos = -1; for (int j = 0; j < BEST_SIZE; j ++) if (pool[i].fitness() > pool[j].fitness() && pool[j].fitness() < worstfitness) { worstfitness = pool[j].fitness(); pos = j; if (pos >= 0) pool[pos] = pool[i]; generation++; // end-while Saake/Schallehn Algorithmen & Datenstrukturen 13 32 Uni Magdeburg, WS 2005/06 245
Auswahl der besten Kandidaten Gen-Pool Auswahl der besten Kandidaten Saake/Schallehn Algorithmen & Datenstrukturen 13 33 Verbesserung des Algorithmus Einfache Verbesserung durch Generierung einer besseren Startbelegung, zum Beispiel pool[0] = packegierignachnutzen(); pool[1] = packegierignachgewicht(); for (int i=2; i < BEST_SIZE; i++) pool[i] = pool[i%2].mutate(); vermindert notwendige Generationenzahl durch Verkürzung der Anlaufphase derartige Kombination von Greedy und genetischen Algorithmen häufig eingesetzt Saake/Schallehn Algorithmen & Datenstrukturen 13 34 Analyse des genetischen Algorithmus Probleme: Findet nicht immer optimale Lösung Optimale Belegung der Parameter nicht trivial Vorteile: Komplexität O(n) Verhältnis zwischen Aufwand und Genauigkeit kann über Parameter gesteuert werden Saake/Schallehn Algorithmen & Datenstrukturen 13 35 246 Uni Magdeburg, WS 2005/06
Lösungen im Vergleich Verfahren Optimale Lösung Aufwand Komplexität Greedy nein + O(n) Backtracking ja - O(2 n ) DP ja +/- O(z) GenAlg nein + O(n) Saake/Schallehn Algorithmen & Datenstrukturen 13 36 Zusammenfassung Lösung des Rucksackproblems mit unterschiedlichen Ansätzen Greedy Backtracking Dynamische Programmierung Genetische Algorithmen 13 Teile und Herrsche bereits behandelt: Sortierverfahren und binäre Suche Saake/Schallehn Algorithmen & Datenstrukturen 13 37 Uni Magdeburg, WS 2005/06 247