8 - 1 Teil 8 Rucksackproblem, Dynamische Programmierung, Branch&Bound ================================================================ Rucksackproblem: Gegeben m Päckchen mit Gewichten g1,g2,...,gm und ein Rucksack mit Kapazität K (alles natürliche Zahlen). Gesucht ist eine Teilmenge der Päckchen, die den Rucksack möglichst gut ausfüllt, ohne die Kapazitätgrenze zu überschreiten. Beispiel Päckchen Gewicht (1) (2) (3) (4) 7 6 4 4 Kapazität K = 16 Was ist die Optimallösung für diesen Datensatz? Wir beginnen mit zwei naheliegenden Lösungsansätzen für das Rucksackproblem: Vollständige Enumeration: Wir probieren alle Teilmengen aus und nehme von denen, die in den Rucksack hineinpassen, die beste. Was sind Argumente für oder gegen die Praktikabilität dieser Strategie? Greedy-Heuristik: Wir fangen mit den großen Päckchen an, wählen stets das nächst kleinere und packen es in den Rucksack, sofern es hineingeht. - Die Motivation dieser Heuristik ist, daß die zum Schluß eingepackten kleineren Päckchen die Ecken besser ausfüllen. Hinweis: Die Greedy-Heuristik die größten Päckchen zuerst ist nicht immer optimal. Immerhin ist es eine Tatsache, daß man auf diese Weise mindestens 50% des maximal möglichen Gewichtes bekommt, man weise dies nach! An Beispielen demonstriere man, daß mehr als 50% von der GreedyHeuristik nicht garantiert werden können. Zwei Anwendungen des Rucksackproblems: Verschnittproblem Gegeben ist ein Rohprodukt bekannter Größe. Aus dem möchte man Fertigprodukte ausschneiden, die in ihrer Größe ebenfalls bekannt sind. Je nachdem für welche Fertigprodukte man sich entscheidet, wird das Rohprodukt mehr oder weniger gut ausgenutzt. Frage: Welche Fertigprodukte soll man auswählen, um möglichst wenig Rest (Verschnitt) zu haben? Offensichtlich ist dieses Verschnittproblem nichts anderes als eine Umformulierung des Rucksackproblems. Verschnittprobleme gibt es in der Praxis in vielerlei Varianten und auch in viel komplexerer Gestalt als der eben genannten. 8 - 2 Reihenfolgeproblem Man hat zwei gleichartige Maschinen. Man hat ferner Jobs, von denen jeder auf einer der Maschinen (gleichgültig auf welcher) zu bearbeiten ist. Ebenfalls bekannt sind die Bearbeitungszeiten der Jobs. Frage: Wie soll man die Jobs auf die beiden Maschinen verteilen, um möglichst früh mit allen Jobs fertig zu sein? [ Man kann an Kopierjobs denken und an zwei gleichartige Kopierer, die für die Jobs zur Verfügung stehen. ] Bearbeitungszeit J1 5 J2 5 J3 5 J4 7 J5 8 Eine Möglichkeit ist m1 m2 |********* J5 **********|****** J2 ****| 0--+--+--+--+--5--+--+--+--+-10--+--+--+--+-15--+--+ |******** J4 ********|**** J1 ******|***** J3 *****| mit Gesamtdauer 17. Aber offensichtlich geht es auch mit Gesamtdauer 15, wenn man nämlich die Jobs J1,J2,J3 von der einen Maschine bearbeiten läßt, J4 und J5 von der anderen. Auch das Reihenfolgeproblem kann als Rucksackproblem interpretiert werden, wie? ------------------------------------------------------------Da unsere ersten beide Ansätze zur Lösung des Rucksackproblems nicht überzeugen (vollständiger Enumeration dauert zu lange, die Greedy-Heuristik ist nicht gut genug), suchen wir nach Besserem: Dynamische Programmierung zur Lösung des Rucksackproblems Die Idee ist rekursives Vorgehen. Wir reduzieren den Datensatz: Statt aller m Päckchen lassen wir nur die ersten i Päckchen zur Konkurrenz zu, i≤m, und statt der vollen Kapazität K lassen wir an Kapazität nur k zu, k≤K. Den Optimalwert des so eingeschränkten Rucksackproblems bezeichnen wir mit fik , i=1,...,m; k=0,...,K 8 - 3 Gesucht ist letztlich die einzige Zahl fmK , d.h. der Optimalwert des Rucksackproblems mit voller Kapazität K und Auswahlmöglichkeit zwischen allen Päckchen. Die fik lassen sich folgendermaßen rekursiv berechnen: Bellmannsche Rekursiongleichung Für den Optimalwert fik des (wie oben erläutert) eingeschränkten Rucksackproblems gilt (warum?) die Rekursionsformel: -----------------------------------| fik = max { fi-1,k , gi + fi-1,k-gi } | ------------------------------------ i=1,...,m k=0,...,K wobei f0k = 0 für alle k und fi0 = 0 für alle i . Für unser obiges Beispiel erhalten wir die folgende Tabelle: i g\k 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16=K --------------------------------------------------------------1 7 | 0 0 0 0 0 0 0 7 7 7 7 7 7 7 7 7 7 | 2 6 | 0 0 0 0 0 0 6 7 7 7 7 7 7 13 13 13 13 | 3 4 | 0 0 0 0 4 4 6 7 7 7 10 11 11 13 13 13 13 | 4 4 | 0 0 0 0 4 4 6 7 8 8 10 11 11 13 14 15 15 (4=m) Mit Dynamischer Optimierung findet man die Optimallösung für das Rucksackproblem. - Ist die Methode aber auch effizient? Dazu klären wir den Rechenaufwand: Das Tableau hat m Zeilen und K Spalten. Pro Eintrag ist eine Addition und ein Vergleich auszuführen. Der Rechenaufwand ist also proportional zum Produkt von m und K O(m*K), und es sieht so aus, als hätten wir den gesuchten schnellen Algorithmus. Aber das ist leider ein Trugschluß: Angenommen, die Kapazität ist 106 und wir haben nur vier Päckchen mit Gewichten wie 7 , 321 , 123477 , 523313 . 8 - 4 Machen wir für diesen Datensatz Dynamische Programmierung wie beschrieben, so haben wir ein Tableau mit einer Million Spalten auszufüllen! Dies ist eine offensichtliche Absurdität. Wir werden später versuchen, den Ansatz der dynamischen Programmierung effizienter zu gestalten, aber zunächst probieren wir etwas anderes. Branch&Bound zur Lösung des Rucksackproblems Wir unterteilen die Lösung in Stufen, und auf jeder Stufe entscheiden wir über ein weiteres Päckchen. Genauer: Auf Stufe i entscheiden wir über das i-te Päckchen (Päckchen i dabei/nicht dabei). Die beiden resultierenden Möglichkeiten werden auf Stufe i+1 einzeln weiterverfolgt. Bei unserem Beispiel mit 4 Päckchen (m=4) Päckchen Gewicht (1) (2) (3) (4) 7 6 4 4 Kapazität K=16 entsteht der folgende Suchbaum: START 1 (7) ¬1 (0) 2 ¬2 2 ¬2 (13) (7) (6) (0) / \ / \ / \ / \ 3 ¬3 3 ¬3 3 ¬3 3 ¬3 (17) (13) (11) (7) (10) (6) (4) (0) / \ / \ / \ / \ / \ / \ / \ / \ 4 ¬4 4 ¬4 4 ¬4 4 ¬4 4 ¬4 4 ¬4 4 ¬4 4 ¬4 (21)(17) (17)(13)(15)(11) (11)(7) (14)(10)(10)(6)(8) (4) (4)(0) Die beste zulässige Lösung hat Gewicht 15 und findet sich auf dem Pfad (1,¬2,3,4). Dieser Suchbaum hat die Tendenz, für wachsende Päckchenzahl gigantische Ausmaße anzunehmen, aber in vielen Fällen kann man ihn (manchmal sogar dramatisch) verkleinern durch Ausnutzen von Bounds: 8 - 5 Bound: Man braucht unterhalb der aktuellen Ecke nicht weiterzusuchen, wenn man dort mit Sicherheit nicht mehr bekommen kann als man ohnehin schon hat. Suche in die Tiefe: Wendet man diesen Bound bei unserem Beispiel an, so erhalten wir den folgenden, stark verkleinerten Suchbaum. Dabei navigieren wir über den Suchbaum nach dem Prinzip Suche in die Tiefe, d.h. wir gehen immer links runter sofern möglich. START 1 (7) 2 ¬2 (13) (7) / \ / 3 ¬3 3 (17) (13) (11) / \ / 4 ¬4 4 (17)(13) (15) | | 1.Lösung 2.Lösung Restgewicht: ¬1 (0) | | | | | | | | | [14] [8] [4] Wir wollen den Rechenaufwand dieses Branch&Bound-Verfahrens bestimmen (zunächst in der reinen Form ohne Bounds). Und das ist sehr einfach: Bei jeder Stufe des Suchbaumes verdoppelt sich die Anzahl der Ecken des Baumes. Bei m zur Auswahl stehenden Päckchen hat allein das unterste Baumniveau 2m (exponentiell viele) Ecken. Mit anderen Worten: Das Verzweigungsverfahren, wie beschrieben in reiner Form angewandt, ist bei größeren Päckchenzahlen m wegen des großen Rechenaufwandes völlig untauglich. Die Frage ist, ob gute Bounds die Situation entscheidend verbessern. Nehmen wir an, wir haben 100 Päckchen (m=100), nehmen wir außerdem (sehr optimistisch) an, daß wir einen extrem guten Bound haben, der die Größe des Suchbaumes auf ein Tausendstel verringert. Dann verringert der Bound den Rechenaufwand von 2100 auf 2100/1000 ≈ 2100/210 = 290 8 - 6 und das ist immer noch eine astronomisch große Zahl. Letztlich sind unsere beiden Lösungsansätze (Dynamische Programmierung und Branch&Bound) unbefriedigend geblieben, sodaß wir die Suche nach schnellen, optimalen Algorithmen aufgeben (aber weiteres zu diesem Thema siehe Teil 10) und uns für den Moment mit Näherungslösungen zufrieden geben. ---------------------------------------------------------------Approximationsschemata beim Rucksackproblem Wir wissen: Die simple Heuristik Die größten Päckchen zuerst garantiert (nur) 50% des maximal möglichen Gewichtes. Die Frage ist naheliegend, ob es nicht ein Verfahren gibt, das ebenfalls schnell arbeitet, das aber eine wesentlich bessere Gütegarantie gibt als 50%. In der Tat gibt es so etwas für das Rucksackproblem. Die Methode, die wir vorstellen wollen, beruht auch auf Dynamischer Programmierung, aber angewandt auf einen variierten Datensatz. Das Unangenehme bei dynamischer Programmierung ist, daß sie mit großen Zahlen (für Kapazität und Gewichte) schlecht fertig wird. Die Idee ist, Kapazität und Gewichte auf einen hinreichend kleinen Bereich proportional abzubilden und erst dann dynamische Programmierung anzuwenden. Dabei bekommen wir allerdings das Problem, daß die Ganzzahligkeit der Daten verloren gehen kann. Nehmen wir etwa die Daten g : 7 , 321 , 123477 , 523313 mit K = 106 so kommen wir praktikable Zahlen, wenn wir durch 1000 dividieren. Aber dann gehen bis zu drei Dezimalstellen verloren, und die mittels dynamischer Programmierung gefundene Lösung ist vermutlich nicht mehr die Optimallösung des Ausgangsproblems. Unsere Hoffnung ist: Die Optimallösung des variierten Datensatzes ist (bezüglich der Original-Gewichte) nicht allzu schlecht, d.h. nur wenig schlechter als die Optimallösung des originalen Datensatzes. Diese Hoffnung ist in der Tat berechtigt, und wir werden im folgenden die Güte der Näherungslösung genauer quantifizieren. Sei gegeben ein Datensatz für das Rucksackproblem: --------------------------| I : g1 , g2 ,..., gm ; K | --------------------------- 8 - 7 Den Optimalwert von I bezeichnen wir mit OPT(I). Ohne Einschränkung der Allgemeinheit können wir OPT(I) ≥ K/2 annehmen (im Falle OPT(I) < K/2 haben alle Päckchen, die einzeln in den Rucksack hineinpassen, zusammen weniger als K/2 Gewicht). Wir wählen nun eine natürliche Zahl n und multiplizieren alle Zahlen im Datensatz I mit m*n/K (Motivation siehe unten) wobei wir nach oben aufrunden, falls die Division durch K nicht aufgeht (warum Aufrunden nach oben?): gi' = ⌈gi*m*n/K⌉ , K' = ⌈ K*m*n/K ⌉ = m*n Auf diese Weise erhalten wir den variierten Datensatz I': -------------------------------| I' : g1' , g2' ,..., gm' ; K' | -------------------------------Die hinter diesen Manipulationen stehende Idee ist folgende: Multiplikation mit m/K ändert die Kapazität auf den Wert m, das Tableau bei dynamischer Programmierung wäre dann quadratisch mxm. Nochmalige Multiplikation mit n ändert die Kapazität auf m*n, und das Tableau bekommt das Format (m,mxn). Der Faktor n ermöglicht es uns also, die Spaltenzahl des Tableaus und damit den Rechenaufwand flexibel zu steuern. Den Datensatz I' lösen wir nun mit dynamischer Programmierung. Der Rechenaufwand bei dynamischer Programmierung hat die Größenordnung Zeilenzahl*Spaltenzahl. Das zu I' gehörende Tableau verursacht also Rechenaufwand O(m²*n) . Ferner erkennt man, daß die für I' optimale Päckchenauswahl jedenfalls zulässig für das Ausgangsproblem I ist. [ Warum? Wichtig ist dabei, daß die Zahlen nach oben gerundet wurden und nicht etwa nach unten. ] Bleibt die Frage, wie gut die Optimallösung von I' bezüglich der Original-Daten (also bezüglich der gegebenen gi) ist. Wir führen eine Bezeichnung ein: Sei An(I) der Originalwert der mittels I' gefundenen Lösung (also die Summe der originalen 8 - 8 Päckchengewichte der für I' gefundenen Lösung). Die Frage ist, wie weit An(I) vom Optimalwert OPT(I) des Problems abweichen kann, wie groß also OPT(I)-An(I) maximal werden kann. Die Antwort ist: OPT(I) - An(I) ≤ K/n Dies ist die entscheidende Ungleichung, deren Gültigkeit wir an einem kleinen Beispiel demonstrieren wollen: (I) g: 40 == 10 == 21 11 ; K=50 (1) Die Optimallösung ist unterstrichen und hat Wert 50. Hätten wir etwa 1/10 als Multiplikationsfaktor, so bekämen wir 4 1 2.1 1.1 ; 4 1 3 = 2 = ; 5 (2) und dann (I') g': K'=5 (3) Eine Optimallösung mit Wert 5 ist unterstrichen. Bezüglich der Originaldaten hat diese Lösung nur den Wert 21+11=32 (statt 50). Wir analysieren allgemein: Von (2) nach (3) ist die maximal mögliche Werteinbuße 1 pro Päckchen (2.1 statt 3 und 1.1 statt 2). Dieser Wertverlust findet schlimmstenfalls bei allen Päckchen statt, also ist der Verlust zwischen (2) und (3) höchstens m. Zwischen (1) und (2) wird mittels Faktor m*n/K proportional reduziert, also ist der insgesamt mögliche Wertverlust höchstens m*K/(m*n) = K/n. Im Zahlenbeispiel wären das 4*10 = 40. Der tatsächlich eingetretene Verlust war nur 50-32=18. Die Güte der mittels I' gefundenen Näherungslösung kann nun einfach ausgerechnet werden. Wir berechnen das Verhältnis von Näherungswert zu Optimalwert: An(I) OPT(I) - K/n K/n ------ ≥ ------------ = 1 - -----OPT(I) OPT(I) OPT(I) (*) ≥ 1 - K/n --- = K/2 1-2/n Bei der Ungleichung (*) erinnern wir uns an die oben gemachte Annahme OPT(I)≥K/2. Insgesamt haben wir folgendes Ergebnis: 8 - 9 Polynomiales Approximationsschema Lösen wir wie oben beschrieben das Rucksackproblem durch Skalieren mit Parameter n und anschließender Dynamischer Programmierung angewandt auf den skalierten Datensatz, so gilt für den relativen Abstand zwischen gefundenem Wert An(I) und theoretischem Wert OPT(I) die Abschätzung ---------------------| An(I) | | ------ ≥ 1 - 2/n | | OPT(I) | ---------------------Der Rechenaufwand O(m²*n) ist dabei polynomial in Päckchenzahl m und Gütemaß n. Beispiel Wir wollen dieses Verfahren an unserem Beispiel (I): g1 7 g2 6 g3 4 g4 4 ; K 16 demonstrieren. Der Optimalwert ist von I ist 15, wie wir wissen. Wir wählen n=3. Der Garantiewert ist dann 1 - 2/n = 1-2/3 = 1/3 wir haben also mindestens Wert 15/3=5 in der folgenden Näherung zu erwarten (was wenig ist im Vergleich zu 15, aber wir haben ein kleines n gewählt). Wir skalieren den Datensatz und berechnen zunächst den Skalierungsfaktor: m*n/K = 4*3/16 = 3/4 Wir müssen die Daten in I also mit 3/4 malnehmen und nach oben runden. Das liefert (I'): g1' 6 g2' g3' 5 3 g4' ; 3 K' 12 Den Datensatz I' lösen wir mittels gewöhnlicher dynamischer Programmierung: 8 - 10 i g'\k 0 1 2 3 4 5 6 7 8 9 10 11 12=K' --------------------------------------------------------1 6 | 0 0 0 0 0 0 6 6 6 6 6 6 6 | 2 5 | 0 0 0 0 0 5 6 6 6 6 6 11 11 | 3 3 | 0 0 0 3 3 5 6 6 8 9 9 11 11 | 4 3 | 0 0 0 3 3 5 6 6 8 9 9 11 12 (=m) Der Optimalwert (im skalierten Problem!) ist 12 und zwar realisiert durch die Päckchen 1,3 und 4. Bezüglich der Originaldaten haben diese Päckchen das Gewicht 7+4+4=15, und das ist der Optimalwert für I! Unsere Skalierungsmethode hat also in diesem Falle sogar den Optimalwert gefunden, und der Garantiewert (5 statt 15) erwies sich als sehr konservativ. -----------------------------------------------------------Verallgemeinertes Rucksackproblem (mit Gewichten und Werten) Gegeben sind n Päckchentypen, von jedem Typ unbeschränkt viele Exemplare. Ein Päckchen vom Typ i hat Gewicht gi und Wert wi. Gesucht ist die Rucksackfüllung, deren Gewicht die Kapazität K nicht überschreitet und die maximalen Gesamtwert hat. Daten: P'typen Wert Gewicht (1)(2)(3)(4) 1 3 5 9 2 3 4 7 Kapazität K = 10 Greedy-Heuristik: Als nächstes Päckchen wähle man stets das spezifisch wertvollste, also das Päckchen, das den Quotienten wi/gi maximiert und das zusammen mit den schon eingefüllten Päckchen die Kapazitätsgrenze K nicht überschreitet. Diese Heuristik ist aber nicht immer optimal, Gegenbeispiel? Wie lautet die Bellmansche Rekursionsgleichung dieser Problemvariante? Man löse das Beispiel per Branch&Bound. Aus der oben angegebenen Regel kann in naheliegender Weise ein Bound abgeleitet werden.
© Copyright 2024 ExpyDoc