Dynamische Programmierung

In der Mathematik und Informatik ist dynamische Programmierung eine Methode, um komplizierte Probleme durch das Zerbrechen von ihnen unten in einfachere Teilprobleme zu beheben. Es ist auf Probleme anwendbar, die die Eigenschaften von überlappenden Teilproblemen ausstellen, die nur ein bisschen kleinerer und optimaler Unterbau (beschrieben unten) sind. Wenn anwendbar, nimmt die Methode viel weniger Zeit als naive Methoden.

Die Schlüsselidee hinter der dynamischen Programmierung ist ziemlich einfach. Im Allgemeinen, um ein gegebenes Problem zu beheben, müssen wir verschiedene Teile des Problems (Teilprobleme) lösen, dann die Lösungen der Teilprobleme zu verbinden, eine gesamte Lösung zu erreichen. Häufig sind viele dieser Teilprobleme wirklich dasselbe. Die dynamische Programmierannäherung bemüht sich, jedes Teilproblem nur einmal zu lösen, so die Anzahl der Berechnung vermindernd: Sobald die Lösung eines gegebenen Teilproblems geschätzt worden ist, wird sie versorgt oder "Merkzettel-ized": Das nächste Mal, wenn dieselbe Lösung erforderlich ist, wird sie einfach nachgeschlagen. Diese Annäherung ist besonders nützlich, wenn die Zahl von sich wiederholenden Teilproblemen exponential als eine Funktion der Größe des Eingangs wächst.

Verfeinernde dynamische Programmierung bedeutet einfach, die Ergebnisse von bestimmten Berechnungen zu versorgen, die später wieder verwendet werden, da die vollendete Berechnung ein Teilproblem einer größeren Berechnung ist. Von unten nach oben ist dynamische Programmierung mit Formulierung einer komplizierten Berechnung als eine rekursive Reihe von einfacheren Berechnungen verbunden.

Geschichte

Dynamische Programmierung des Begriffes wurde in den 1940er Jahren von Richard Bellman ursprünglich verwendet, um den Prozess zu beschreiben, Probleme zu beheben, wo man die besten Entscheidungen nacheinander finden muss. Vor 1953 hat er das zur modernen Bedeutung raffiniert, sich spezifisch auf nistende kleinere Entscheidungsprobleme innerhalb von größeren Entscheidungen beziehend, und das Feld wurde danach durch den IEEE als eine Systemanalyse und Technikthema anerkannt. Der Beitrag von Bellman wird im Namen der Gleichung von Bellman, eines Hauptergebnisses der dynamischen Programmierung nicht vergessen, die ein Optimierungsproblem in der rekursiven Form neu formuliert.

Das dynamische Wort wurde vom Öffentlichen Ausrufer gewählt, um den zeitändernden Aspekt der Probleme zu gewinnen, und weil es eindrucksvoll geklungen hat. Die Wortprogrammierung hat sich auf den Gebrauch der Methode bezogen, ein optimales Programm, im Sinne einer militärischen Liste für die Ausbildung oder Logistik zu finden. Dieser Gebrauch ist dasselbe als das in den Ausdrücken geradlinige Programmierung und mathematische Programmierung, ein Synonym für die mathematische Optimierung.

Übersicht

Dynamische Programmierung ist sowohl eine mathematische Optimierungsmethode als auch ein Computerprogrammierverfahren. In beiden Zusammenhängen bezieht es sich auf die Vereinfachung eines komplizierten Problems durch das Zerbrechen davon unten in einfachere Teilprobleme auf eine rekursive Weise. Während einige Entscheidungsprobleme einzeln dieser Weg nicht genommen werden können, brechen Entscheidungen, die mehrere Punkte rechtzeitig abmessen, häufig rekursiv auseinander; öffentlicher Ausrufer hat das den "Grundsatz von Optimality" genannt. Ebenfalls, in der Informatik, wie man sagt, hat ein Problem, das rekursiv gebrochen werden kann, optimalen Unterbau.

Wenn Teilprobleme rekursiv innerhalb von größeren Problemen verschachtelt werden können, so dass dynamische Programmierverfahren anwendbar sind, dann gibt es eine Beziehung zwischen dem Wert des größeren Problems und den Werten der Teilprobleme. In der Optimierungsliteratur wird diese Beziehung die Gleichung des Öffentlichen Ausrufers genannt.

Dynamische Programmierung in der mathematischen Optimierung

In Bezug auf die mathematische Optimierung bezieht sich dynamische Programmierung gewöhnlich auf die Vereinfachung einer Entscheidung durch das Zerbrechen davon unten in eine Folge von Entscheidungsschritten mit der Zeit. Das wird durch das Definieren einer Folge von Wertfunktionen V, V..., V, mit einem Argument y das Darstellen des Staates des Systems zuweilen ich von 1 bis n getan. Die Definition V (y) ist der Wert, der im Staat y im letzten Mal n erhalten ist. Die Werte V in früheren Zeiten i = n −1, n − 2..., 2, 1 kann durch das Arbeiten umgekehrt gefunden werden, das Verwenden einer rekursiven Beziehung hat die Gleichung des Öffentlichen Ausrufers genannt. Weil ich = 2..., n, V an jedem Staat y von V berechnet werde, indem ich eine einfache Funktion (gewöhnlich die Summe) des Gewinns von der Entscheidung i &minus maximiere; 1 und die Funktion V am neuen Staat des Systems, wenn diese Entscheidung getroffen wird. Seitdem V ist bereits für die erforderlichen Staaten berechnet worden, die obengenannte Operation trägt V für jene Staaten. Schließlich, V am anfänglichen Staat des Systems ist der Wert der optimalen Lösung. Die optimalen Werte der Entscheidungsvariablen, können eins nach dem anderen, durch das Verfolgen zurück der bereits durchgeführten Berechnungen wieder erlangt werden.

Dynamische Programmierung in der Computerprogrammierung

Es gibt zwei Schlüsselattribute, die ein Problem in der Größenordnung von der dynamischen Programmierung haben muss, um anwendbar zu sein: optimaler Unterbau und überlappende Teilprobleme. Jedoch, wenn die überlappenden Probleme viel kleiner sind als das ursprüngliche Problem, wird die Strategie genannt "teilen sich und siegen" aber nicht "dynamische Programmierung". Das ist, warum mergesort, Schnellsortierung und Entdeckung aller Matchs eines regelmäßigen Ausdrucks als dynamische Programmierprobleme nicht klassifiziert werden.

Optimaler Unterbau bedeutet, dass die Lösung eines gegebenen Optimierungsproblems durch die Kombination von optimalen Lösungen seiner Teilprobleme erhalten werden kann. Folglich ist der erste Schritt zum Planen einer dynamischen Programmierlösung zu überprüfen, ob das Problem solchen optimalen Unterbau ausstellt. Solche optimalen Unterbauten werden gewöhnlich mittels recursion beschrieben. Zum Beispiel, in Anbetracht eines Graphen G = (V, E), stellt der kürzeste Pfad p von einem Scheitelpunkt u zu einem Scheitelpunkt v optimalen Unterbau aus: Nehmen Sie jeden Zwischenscheitelpunkt w auf diesem kürzesten Pfad p. Wenn p aufrichtig der kürzeste Pfad ist, dann ist der Pfad p von u bis w und p von w bis v tatsächlich die kürzesten Pfade zwischen den entsprechenden Scheitelpunkten (durch das einfache Cut-And-Paste-Argument, das in CLRS beschrieben ist). Folglich kann man die Lösung leicht formulieren, um kürzeste Pfade auf eine rekursive Weise zu finden, die ist, was der Algorithmus von Ford des Öffentlichen Ausrufers oder der Algorithmus von Floyd-Warshall tun.

Überlappende Teilprobleme bedeuten, dass der Raum von Teilproblemen klein sein muss, d. h. sollte jeder rekursive Algorithmus, der das Problem behebt, dieselben Teilprobleme immer wieder lösen, anstatt neue Teilprobleme zu erzeugen. Denken Sie zum Beispiel die rekursive Formulierung, für die Reihe von Fibonacci zu erzeugen: F = F + F, mit dem Grundfall F = F = 1. Dann F = F + F und F = F + F. Jetzt wird F in den rekursiven Subbäumen von beiden F sowie F gelöst. Wenn auch die Gesamtzahl von Teilproblemen wirklich klein ist (nur 43 von ihnen), enden wir damit, dieselben Probleme immer wieder zu beheben, wenn wir eine naive rekursive Lösung wie das annehmen. Dynamische Programmierung zieht diese Tatsache in Betracht und löst jedes Teilproblem nur einmal. Bemerken Sie, dass die Teilprobleme nur (normalerweise genommen ein bisschen kleiner sein müssen, um einen unveränderlichen zusätzlichen Faktor zu bedeuten), als das größere Problem; wenn sie ein multiplicative kleinerer Faktor sind, wird das Problem als dynamische Programmierung nicht mehr klassifiziert.

Das kann auf jede von zwei Weisen erreicht werden:

  • Verfeinernde Annäherung: Das ist der direkte radioaktive Niederschlag der rekursiven Formulierung jedes Problems. Wenn die Lösung eines Problems rekursiv mit der Lösung seiner Teilprobleme formuliert werden kann, und wenn seine Teilprobleme überlappen, dann kann man leicht memoize oder die Lösungen der Teilprobleme in einem Tisch versorgen. Wann auch immer wir versuchen, ein neues Teilproblem zu lösen, überprüfen wir zuerst den Tisch, um zu sehen, ob er bereits gelöst wird. Wenn eine Lösung registriert worden ist, können wir sie direkt verwenden, sonst lösen wir das Teilproblem und fügen seine Lösung des Tisches hinzu.
  • Von unten nach oben Annäherung: Sobald wir die Lösung eines Problems rekursiv als in Bezug auf seine Teilprobleme formulieren, können wir versuchen, das Problem in von unten nach oben Mode wiederzuformulieren: Versuchen Sie, die Teilprobleme zuerst zu lösen, und verwenden Sie ihre Lösungen, aufzubauen und Lösungen größerer Teilprobleme zu erreichen. Das wird auch gewöhnlich in einer tabellarischen Form durch das wiederholende Erzeugen von Lösungen größerer und größerer Teilprobleme durch das Verwenden der Lösungen kleiner Teilprobleme getan. Zum Beispiel, wenn wir bereits die Werte von F und F wissen, können wir den Wert von F direkt berechnen.

Einige Programmiersprachen können automatisch memoize das Ergebnis eines Funktionsanrufs mit einem besonderen Satz von Argumenten, um Einschätzung des Anrufs namentlich zu beschleunigen (dieser Mechanismus wird Anruf durch das Bedürfnis genannt). Einige Sprachen machen es möglich tragbar (z.B Schema, Common Lisp oder Perl), ein Bedürfnis spezielle Erweiterungen (z.B. C ++, sieh). Einige Sprachen ließen automatischen memoization in, wie vorgelegte Einleitung und J bauen, der memoization mit dem M. Adverb unterstützt. Jedenfalls ist das nur für eine Verweisungs-durchsichtige Funktion möglich.

Beispiel: Mathematische Optimierung

Optimaler Verbrauch und das Sparen

Ein mathematisches Optimierungsproblem, das häufig im Unterrichten dynamischer Programmierung Wirtschaftswissenschaftlern verwendet wird (weil es mit der Hand gelöst werden kann) betrifft einen Verbraucher, der im Laufe der Perioden lebt und entscheiden muss, wie viel man sich verzehrt, und wie viel man in jeder Periode spart.

Lassen Sie, Verbrauch in der Periode zu sein, und Verbrauchsertrag-Dienstprogramm so lange die Verbraucherleben anzunehmen. Nehmen Sie an, dass der Verbraucher ungeduldig ist, so dass er zukünftiges Dienstprogramm durch einen Faktor jede Periode, wo rabattiert

: unterwerfen Sie für den ganzen

Schriftlich dieser Weg, das Problem sieht kompliziert aus, weil es das Lösen für alle auserlesenen Variablen und gleichzeitig einschließt. (Bemerken Sie, dass das nicht eine auserlesene Variable ist — wird das anfängliche Kapital des Verbrauchers, wie gegeben, genommen.)

Die dynamische Programmierannäherung an das Beheben dieses Problems ist verbunden es in eine Folge von kleineren Entscheidungen auseinander zu brechen. Um so zu tun, definieren wir eine Folge von Wertfunktionen, für die den Wert vertreten, jeden Betrag des Kapitals jedes Mal zu haben. Bemerken Sie, dass, d. h. es (durch die Annahme) kein Dienstprogramm davon gibt, Kapital nach dem Tod zu haben.

Der Wert jeder Menge des Kapitals in jedem vorherigen Mal kann durch die rückwärts gerichtete Induktion mit der Gleichung des Öffentlichen Ausrufers berechnet werden. In diesem Problem, für jeden, ist die Gleichung des Öffentlichen Ausrufers

:

Dieses Problem ist viel einfacher als dasjenige, das wir vorher niedergeschrieben haben, weil es nur zwei Entscheidungsvariablen einschließt, und. Intuitiv, anstatt seinen ganzen Lebensplan bei der Geburt zu wählen, kann der Verbraucher Dinge ein Schritt auf einmal nehmen. In der Zeit wird sein aktuelles Kapital gegeben, und er muss nur aktuellen Verbrauch und das Sparen wählen.

Um wirklich dieses Problem zu beheben, arbeiten wir umgekehrt. Für die Einfachheit wird das aktuelle Niveau des Kapitals als angezeigt. ist bereits so mit der Gleichung des Öffentlichen Ausrufers bekannt, sobald wir und so weiter rechnen können, bis wir dazu kommen, der der Wert des anfänglichen Entscheidungsproblems für die ganze Lebenszeit ist. Mit anderen Worten, sobald wir wissen, können wir rechnen, der das Maximum dessen ist, wo die auserlesene Variable ist und.

Umgekehrt arbeitend, kann es gezeigt werden, dass die Wertfunktion in der Zeit ist

:

wo jeder eine Konstante ist, und der optimale Betrag, um sich in der Zeit zu verzehren, ist

:

der zu vereinfacht werden kann

: und, und, usw.

Wir sehen, dass es optimal ist, um einen größeren Bruchteil des aktuellen Reichtums zu verbrauchen, weil man älter wird, schließlich den ganzen restlichen Reichtum in der Periode, der letzten Periode des Lebens verbrauchend.

Beispiele: Computeralgorithmen

Der Algorithmus von Dijkstra für das kürzeste Pfad-Problem

Aus einem dynamischen Programmiergesichtspunkt ist der Algorithmus von Dijkstra für das kürzeste Pfad-Problem ein aufeinander folgendes Annäherungsschema, das die dynamische programmierende funktionelle Gleichung für das kürzeste Pfad-Problem durch die reichende Methode löst.

Tatsächlich, die Erklärung von Dijkstra der Logik hinter dem Algorithmus, nämlich

ist ein Paraphrasieren des berühmten Grundsatzes des öffentlichen Ausrufers von Optimality im Zusammenhang des kürzesten Pfad-Problems.

Folge von Fibonacci

Hier ist eine naive Durchführung einer Funktion, die das n-te Mitglied der Folge von Fibonacci, gestützt direkt auf der mathematischen Definition findet:

Funktionsflunkerei (n)

wenn n = 0 Rückkehr 0

wenn n = 1 Rückkehr 1

geben Sie Flunkerei (n  1) + Flunkerei (n  2) zurück

Bemerken Sie, dass, wenn wir, sagen wir, rufen, wir einen Anruf-Baum erzeugen, der die Funktion auf demselben Wert viele verschiedene Male nennt:

Insbesondere wurde dreimal von Kratzer berechnet. In größeren Beispielen werden noch viele Werte, oder Teilprobleme, wiederberechnet, zu einem Exponentialzeitalgorithmus führend.

Nehmen Sie jetzt an, dass wir einen einfachen Karte-Gegenstand haben, ist M, die jeden Wert davon kartografisch darstellt, bereits zu seinem Ergebnis berechnet worden, und wir modifizieren unsere Funktion, es zu verwenden und es zu aktualisieren. Die resultierende Funktion verlangt nur O (n) Zeit statt der Exponentialzeit:

var M: = Karte (0  0, 1  1)

Funktionsflunkerei (n)

wenn Karte M Schlüssel n nicht enthält

M [n]: = Flunkerei (n  1) + Flunkerei (n  2)

geben Sie M [n] zurück

Diese Technik, Werte zu sparen, die bereits berechnet worden sind, wird memoization genannt; das ist die verfeinernde Annäherung, da wir zuerst das Problem in Teilprobleme brechen und dann berechnen und Werte versorgen.

Darin nähern sich von unten nach oben wir berechnen die kleineren Werte von ersten, bauen dann größere Werte von ihnen. Diese Methode verwendet auch O (n) Zeit, da es eine Schleife enthält, die n  1mal wiederholt, jedoch nimmt es nur unveränderlich (O (1)) Raum im Gegensatz zur verfeinernden Annäherung, die verlangt, dass O (n) Raum die Karte versorgt.

Funktionsflunkerei (n)

var previousFib: = 0, currentFib: = 1

wenn n = 0

geben Sie 0 zurück

wiederholen Sie sonst n  1mal//Schleife wird wenn n=1 ausgelassen

var newFib: = previousFib + currentFib

previousFib: = currentFib

currentFib: = newFib

geben Sie currentFib zurück

Sowohl in diesen Beispielen berechnen wir nur eine Zeit, als auch in verwenden sie dann, um beide zu berechnen und, anstatt sie jedes Mal zu schätzen, wenn jeder von ihnen bewertet wird.

Bemerken Sie, dass die obengenannte Methode wirklich für großen n Zeit in Anspruch nimmt, weil Hinzufügung von zwei ganzen Zahlen mit Bit jeder Zeit braucht. Außerdem gibt es eine geschlossene Form für die Folge von Fibonacci, die als die Formel von Binet bekannt ist, von der der-Th-Begriff in ungefähr der Zeit geschätzt werden kann, die effizienter ist als die obengenannte dynamische Programmiertechnik. Jedoch gibt das einfache Wiederauftreten direkt die Matrixform, die ungefähr Algorithmus durch die schnelle Matrix exponentiation führt.

Ein Typ der erwogenen 0-1 Matrix

Denken Sie das Problem, Werte, entweder Null oder ein, zu den Positionen einer Matrix, mit sogar zuzuteilen, so dass jede Reihe und jede Säule genau Nullen und enthalten. Wir fragen, wie viel verschiedene Anweisungen dort für einen gegebenen sind. Zum Beispiel, wenn, vier mögliche Lösungen sind

:

0 & 1 & 0 & 1 \\

1 & 0 & 1 & 0 \\

0 & 1 & 0 & 1 \\

1 & 0 & 1 & 0

\end {bmatrix} \text {und} \begin {bmatrix }\

0 & 0 & 1 & 1 \\

0 & 0 & 1 & 1 \\

1 & 1 & 0 & 0 \\

1 & 1 & 0 & 0

\end {bmatrix} \text {und} \begin {bmatrix }\1 & 1 & 0 & 0 \\0 & 0 & 1 & 1 \\1 & 1 & 0 & 0 \\

0 & 0 & 1 & 1

\end {bmatrix} \text {und} \begin {bmatrix }\

1 & 0 & 0 & 1 \\

0 & 1 & 1 & 0 \\

0 & 1 & 1 & 0 \\

1 & 0 & 0 & 1

\end {bmatrix}. </Mathematik>

Es gibt mindestens drei mögliche Annäherungen: rohe Gewalt, das Zurückverfolgen und die dynamische Programmierung.

Rohe Gewalt besteht daraus, alle Anweisungen von Nullen und zu überprüfen und diejenigen aufzuzählen, die Reihen und Säulen (Nullen und) erwogen haben. Da es mögliche Anweisungen gibt, ist diese Strategie außer vielleicht bis dazu nicht praktisch.

Das Zurückverfolgen für dieses Problem besteht daraus, eine Ordnung der Matrixelemente zu wählen und rekursiv oder Nullen zu legen, während es überprüft, dass in jeder Reihe und Säule die Zahl der Elemente, die plus die Zahl von oder Nullen nicht zugeteilt worden sind, beide mindestens n / 2 ist. Während hoch entwickelter, als rohe Gewalt wird diese Annäherung jede Lösung einmal besuchen, es unpraktisch für den n größer machend, als sechs, da die Zahl von Lösungen bereits 116963796250 für n = 8 ist, wie wir sehen werden.

Dynamische Programmierung macht es möglich, die Zahl von Lösungen aufzuzählen, ohne sie alle zu besuchen. Stellen Sie sich vor, Werte für die erste Reihe denselben Weg zurückzuverfolgen - welche Information würden wir über die restlichen Reihen verlangen, um im Stande zu sein, die Lösungen genau aufzuzählen, die für jede erste Reihe Werte erhalten sind? Wir denken Ausschüsse, wo, wessen Reihen Nullen und enthalten. Die Funktion f, auf den memoization Karte-Vektoren von n Paaren von ganzen Zahlen zur Zahl von zulässigen Ausschüssen (Lösungen) angewandt wird. Es gibt ein Paar für jede Säule, und seine zwei Bestandteile zeigen beziehungsweise die Zahl von und Nullen an, die noch in diese Säule gelegt werden müssen. Wir suchen den Wert (Argumente oder ein Vektor von Elementen). Der Prozess der Teilproblem-Entwicklung ist mit dem Wiederholen über jede von möglichen Anweisungen für die Spitzenreihe des Ausschusses und Durchgehen jeder Säule verbunden, ein vom passenden Element des Paares für diese Säule je nachdem Abstriche machend, ob die Anweisung für die Spitzenreihe eine Null oder diejenige an dieser Position enthalten hat. Wenn irgendwelche der Ergebnisse negativ sind, dann ist die Anweisung ungültig und trägt zum Satz von Lösungen (recursion Halt) nicht bei. Sonst haben wir eine Anweisung für die Spitzenreihe des Ausschusses und schätzen rekursiv die Zahl von Lösungen des restlichen Ausschusses, die Zahlen von Lösungen für jede zulässige Anweisung der Spitzenreihe hinzufügend und die Summe zurückgebend, die memoized ist. Der Grundfall ist das triviale Teilproblem, das für einen Ausschuss vorkommt. Die Zahl von Lösungen für diesen Ausschuss ist entweder Null oder ein, je nachdem, ob der Vektor eine Versetzung und Paare ist oder nicht.

Zum Beispiel, in den zwei über den Folgen von Vektoren gezeigten Ausschüssen würde sein

((2, 2) (2, 2) (2, 2) (2, 2)) ((2, 2) (2, 2) (2, 2) (2, 2)) k = 4

0 1 0 1 0 0 1 1

((1, 2) (2, 1) (1, 2) (2, 1)) ((1, 2) (1, 2) (2, 1) (2, 1)) k = 3

1 0 1 0 0 0 1 1

((1, 1) (1, 1) (1, 1) (1, 1)) ((0, 2) (0, 2) (2, 0) (2, 0)) k = 2

0 1 0 1 1 1 0 0

((0, 1) (1, 0) (0, 1) (1, 0)) ((0, 1) (0, 1) (1, 0) (1, 0)) k = 1

1 0 1 0 1 1 0 0

((0, 0) (0, 0) (0, 0) (0, 0)) ((0, 0) (0, 0), (0, 0) (0, 0))

</PRE>

Die Zahl von Lösungen ist

:

Verbindungen zur AHORN-Durchführung der dynamischen Programmierannäherung können unter den Links gefunden werden.

Damebrett

Denken Sie ein Damebrett mit n × n Quadrate und eine Kostenfunktion c (ich, j), der Kosten zurückgibt, die mit dem Quadrat i, j (ich vereinigt sind, die Reihe, j seiend, die Säule zu sein). Zum Beispiel (auf 5 × 5 Damebrett),

So c (1, 3) = 5

Lassen Sie uns sagen, dass Sie einen Kontrolleur hatten, der an jedem Quadrat auf der ersten Reihe anfangen konnte (d. h., Reihe) und Sie den kürzesten Pfad haben wissen wollen (Summe der Kosten der besuchten Quadrate sind an einem Minimum), zur letzten Reihe zu kommen, annehmend, dass sich der Kontrolleur nur diagonal verlassen vorwärts, diagonal richtig fortgeschritten, oder aufrichtig bewegen konnte. D. h. ein Kontrolleur auf (1,3) kann sich zu (2,2), (2,3) oder (2,4) bewegen.

Dieses Problem stellt optimalen Unterbau aus. D. h. die Lösung des kompletten Problems verlässt sich auf Lösungen von Teilproblemen. Lassen Sie uns eine Funktion q (ich, j) als definieren

:q (ich, j) = die minimalen Kosten, um Quadrat (ich, j) zu erreichen

Wenn wir die Werte dieser Funktion für alle Quadrate an der Reihe n finden können, picken wir das Minimum auf und folgen diesem Pfad umgekehrt, um den kürzesten Pfad zu bekommen.

Bemerken Sie, dass q (ich, j) den minimalen Kosten gleich ist, um zu einigen der drei Quadrate darunter zu kommen (da diejenigen die einzigen Quadrate sind, die es erreichen können) plus c (ich, j). Zum Beispiel:

:

Lassen Sie uns jetzt q (ich, j) in etwas allgemeineren Begriffen definieren:

:

Die erste Linie dieser Gleichung soll dort das rekursive Eigentum einfacher machen (wenn, sich mit den Rändern befassend, so brauchen wir nur einen recursion). Die zweite Linie sagt, was zufällig in der letzten Reihe, einen Grundfall zur Verfügung stellt. Die dritte Linie, der recursion, ist der wichtige Teil. Es ist dem A, B, C, D Beispiel ähnlich. Aus dieser Definition können wir einen aufrichtigen rekursiven Code für q (ich, j) machen. Im folgenden Pseudocode ist n die Größe des Ausschusses, ist die Kostenfunktion, und gibt das Minimum mehrerer Werte zurück:

fungieren Sie minCost (ich, j)

wenn j &lt; 1 oder j> n

geben Sie Unendlichkeit zurück

sonst, wenn ich = 1

geben Sie c (ich, j) zurück

sonst

geben Sie Minute (minCost (i-1, j-1), minCost (i-1, j), minCost (i-1, j+1)) + c (ich, j) zurück

Es sollte bemerkt werden, dass diese Funktion nur den Pfad-gekosteten, nicht den wirklichen Pfad schätzt. Wir werden zum Pfad bald kommen. Das, wie das Fibonacci-Zahl-Beispiel, ist furchtbar langsam, da es Berge der Zeit ausgibt, dieselben kürzesten Pfade immer wieder wieder rechnend. Jedoch können wir es viel schneller in von unten nach oben Mode schätzen, wenn wir Pfad-Kosten in einer zweidimensionalen Reihe versorgen, anstatt eine Funktion zu verwenden. Das vermeidet Wiederberechnung; vor der Computerwissenschaft der Kosten eines Pfads überprüfen wir die Reihe, um zu sehen, ob die Pfad-Kosten bereits dort sind.

Wir müssen auch wissen, wie der wirkliche kürzeste Pfad ist. Um das zu tun, verwenden wir eine andere Reihe, eine Vorgänger-Reihe. Diese Reihe versorgt implizit den Pfad zu jedem Quadrat s durch die Speicherung des vorherigen Knotens auf dem kürzesten Pfad zu s, d. h. des Vorgängers. Den Pfad, wir lookup der Vorgänger von s, dann der Vorgänger dieses Quadrats, dann der Vorgänger dieses Quadrats und so weiter wieder aufzubauen, bis wir das Startquadrat erreichen. Denken Sie den folgenden Code:

fungieren Sie computeShortestPathArrays

für x von 1 bis n

q [1, x]: = c (1, x)

für y von 1 bis n

q [y, 0]: = Unendlichkeit

q [y, n + 1]: = Unendlichkeit

für y von 2 bis n

für x von 1 bis n

m: = Minute (q [y-1, x-1], q [y-1, x], q [y-1, x+1])

q [y, x]: = M + c (y, x)

wenn M = q [y-1, x-1]

p [y, x]: =-1

sonst, wenn M = q [y-1, x]

p [y, x]: = 0

sonst

p [y, x]: = 1

Jetzt ist der Rest eine einfache Sache, das Minimum zu finden und es zu drucken.

fungieren Sie computeShortestPath

computeShortestPathArrays

minIndex: = 1

Minute: = q [n, 1]

weil ich von 2 bis n

wenn q [n, ich] &lt; Minute

minIndex: = ich

Minute: = q [n, ich]

printPath (n, minIndex)

fungieren Sie printPath (y, x)

Druck (x)

Druck ("&lt; -")

wenn y = 2

Druck (x + p [y, x])

sonst

printPath (y-1, x + p [y, x])

Folge-Anordnung

In der Genetik ist Folge-Anordnung eine wichtige Anwendung, wo dynamische Programmierung notwendig ist. Gewöhnlich besteht das Problem daraus sich zu verwandeln eine Folge in ein anderes Verwenden editieren Operationen, die ersetzen, einfügen, oder ein Element entfernen. Jede Operation hat verbundene Kosten, und die Absicht ist zu finden, dass die Folge dessen mit den niedrigsten Gesamtkosten editiert.

Das Problem kann natürlich als ein recursion festgesetzt werden, eine Folge A wird in eine Folge B durch auch optimal editiert:

  1. das Einfügen des ersten Charakters von B und das Durchführen einer optimalen Anordnung von A und des Schwanzes von B
  2. das Löschen des ersten Charakters von A und das Durchführen der optimalen Anordnung des Schwanzes von A und B
  3. das Ersetzen des ersten Charakters mit dem ersten Charakter von B und das Durchführen optimaler Anordnungen der Schwänze von A und B.

Die teilweisen Anordnungen können in einer Matrix tabellarisiert werden, wo Zelle (ich, j) die Kosten der optimalen Anordnung [1 enthält.. i] zu B [1.. j]. Die Kosten in der Zelle (ich, j) können durch das Hinzufügen der Kosten der relevanten Operationen zu den Kosten seiner benachbarten Zellen und das Auswählen des Optimums berechnet werden.

Verschiedene Varianten bestehen, sehen Algorithmus des Schmieds-Fährmannes und Needleman-Wunsch Algorithmus.

Turm des Hanoier Rätsels

Der Turm Hanois oder die Türme Hanois sind ein mathematisches Spiel oder Rätsel. Es besteht aus drei Stangen und mehreren Platten verschiedener Größen, die auf jede Stange gleiten können. Das Rätsel fängt mit den Platten in einem ordentlichen Stapel an, der der Größe auf einer Stange, das kleinste oben in aufsteigender Reihenfolge ist, so eine konische Gestalt machend.

Das Ziel des Rätsels ist, den kompletten Stapel zu einer anderen Stange zu bewegen, den folgenden Regeln folgend:

  • Nur eine Platte kann auf einmal bewegt werden.
  • Jede Bewegung besteht daraus, die obere Platte von einer der Stangen zu nehmen und es auf eine andere Stange oben auf den anderen Platten gleiten zu lassen, die bereits auf dieser Stange da sein können.
  • Keine Platte darf oben auf einer kleineren Platte gelegt werden.

Die dynamische Programmierlösung besteht daraus, die funktionelle Gleichung zu lösen

: S (n, h, t) = S (n-1, h, nicht (h, t)); S (1, h, t); S (n-1, nicht (h, t), t)

wo n anzeigt, dass die Zahl von zu bewegenden Platten, h anzeigt, dass die Hausstange, t anzeigt, dass die Zielstange, nicht (h, t) die dritte Stange (weder h noch t) anzeigt, ";" zeigt Verkettung und an

: S (n, h, t): = Lösung eines Problems, das aus n Platten besteht, die von der Stange h zur Stange t bewegt werden sollen.

Bemerken Sie, dass für n=1 das Problem, nämlich S trivial ist (1, h, t) = "bewegen eine Platte von der Stange h zur Stange t" (es gibt nur eine Platte verlassen).

Die Zahl von durch diese Lösung erforderlichen Bewegungen ist 2 &minus; 1. Wenn das Ziel ist, die Zahl von Bewegungen zu maximieren (ohne Rad zu fahren) dann, ist die dynamische programmierende funktionelle Gleichung ein bisschen mehr kompliziert und 3 &minus; 1 Bewegungen sind erforderlich.

Ei-Fallen-Rätsel

Der folgende ist eine Beschreibung des Beispiels dieses berühmten Rätsels, das n=2 Eier und ein Gebäude mit H=36 Stöcken einschließt:

:Suppose, den wir wissen möchten, welche Geschichten in einem 36-stöckigen Gebäude sicher sind, Eier von fallen zu lassen, und der die Eier veranlassen wird, auf der Landung zu brechen. Wir machen einige Annahmen:

:* Ein Ei, das einen Fall überlebt, kann wieder verwendet werden.

:* Ein gebrochenes Ei muss verworfen werden.

:* Die Wirkung eines Falls ist dasselbe für alle Eier.

:* Wenn ein Ei, wenn fallen gelassen, bricht, dann würde es, wenn fallen gelassen, von einem höheren Fenster brechen.

:* Wenn ein Ei einen Fall dann überlebt, würde es einen kürzeren Fall überleben.

:* Es wird nicht ausgeschlossen, dass die Fenster des Erdgeschosses Eier brechen, noch es ausgeschlossen wird, dass die 36.-stöckigen Fenster kein Ei veranlassen zu brechen.

: Wenn nur ein Ei verfügbar ist und wir überzeugt sein möchten, das richtige Ergebnis zu erhalten, kann das Experiment auf nur eine Weise ausgeführt werden. Lassen Sie das Ei vom Fenster des Erdgeschosses fallen; wenn es überlebt, lassen Sie es vom Fenster des zweiten Stockwerks fallen. Machen Sie aufwärts weiter, bis es bricht. Im Grenzfall kann diese Methode 36 Exkremente verlangen. Nehmen Sie an, dass 2 Eier verfügbar sind. Wie ist kleinste Zahl von Ei-Exkrementen, die, wie man versichert, arbeitet in allen Fällen?

Um eine dynamische programmierende funktionelle Gleichung für dieses Rätsel abzuleiten, lassen Sie den Staat des dynamischen Programmiermodells ein Paar s = (n, k), wo sein

: n = Zahl von Testeiern verfügbar, n = 0, 1, 2, 3..., N &minus; 1.

: k = Zahl von (konsekutiv)-Stöcken noch, um, k = 0, 1, 2..., H &minus geprüft zu werden; 1.

Zum Beispiel, s = (2,6) zeigt an, dass zwei Testeier verfügbar sind und 6 (aufeinander folgende) Stöcke noch geprüft werden sollen. Der anfängliche Staat des Prozesses ist s = (N, H), wo N die Zahl von am Anfang des Experimentes verfügbaren Testeiern anzeigt. Der Prozess endet auch, wenn es keine Testeier mehr (n = 0) gibt, oder wenn k = 0, welch auch immer zuerst vorkommt. Wenn Beendigung am Staat s = (0, k) und k> 0 vorkommt, dann hat der Test gescheitert.

Lassen Sie jetzt

: W (n, k): = minimale Zahl von Proben, die erforderlich sind, den Wert des kritischen Fußbodens laut des Größten anzunehmenden Unfalls zu identifizieren vorausgesetzt, dass der Prozess im Staat s = (n, k) ist.

Dann kann ihm das gezeigt werden

: W (n, k) = 1 + Minute {max (W (n &minus; 1, x &minus; 1), W (n, k &minus; x)): x in {1, 2..., k}}, n = 2..., N; k = 2, 3, 4..., H

mit W (n, 1) = 1 für den ganzen n> 0 und W (1, k) = k für den ganzen k. Es ist leicht, diese Gleichung wiederholend durch die systematische Erhöhung der Werte von n und k zu lösen.

Eine interaktive Online-Möglichkeit ist für das Experimentieren mit diesem Modell sowie mit anderen Versionen dieses Rätsels verfügbar (z.B, wenn das Ziel ist, den erwarteten Wert der Zahl von Proben zu minimieren.

Algorithmen, die dynamische Programmierung verwenden

  • Wiederkehrende Lösungen von Gitter-Modellen für die PROTEIN-DNA, die bindet
  • Rückwärts gerichtete Induktion als eine Lösungsmethode für die diskrete Zeit des begrenzten Horizonts dynamische Optimierungsprobleme
  • Die Methode von unentschiedenen Koeffizienten kann verwendet werden, um die Gleichung des Öffentlichen Ausrufers im unendlichen Horizont, diskrete Zeit, rabattiert, Zeit-Invariant dynamische Optimierungsprobleme zu lösen
  • Viele Schnur-Algorithmen einschließlich der längsten allgemeinen Subfolge, längsten zunehmenden Subfolge, längsten allgemeinen Teilkette, Entfernung von Levenshtein (editieren Entfernung).
  • Viele algorithmische Probleme auf Graphen können effizient für Graphen von begrenztem treewidth oder begrenzter Clique-Breite durch das Verwenden dynamischer Programmierung auf einer Baumzergliederung des Graphen behoben werden.
  • Der Cocke-Younger-Kasami (CYK) Algorithmus, der bestimmt, ob und wie eine gegebene Schnur durch eine gegebene Grammatik ohne Zusammenhänge erzeugt werden kann
  • Der Wortverpackungsalgorithmus von Knuth, der Zerlumptkeit wenn Wortverpackungstext minimiert
  • Der Gebrauch von Umstellungstischen und Widerlegungstischen im Computerschach
  • Der Viterbi Algorithmus (verwendet für verborgene Modelle von Markov)
  • Der Earley Algorithmus (ein Typ der Karte parser)
  • Der Needleman-Wunsch und die anderen Algorithmen, die in bioinformatics, einschließlich der Folge-Anordnung, Strukturanordnung, RNS-Struktur-Vorhersage verwendet sind.
  • Die Vollpaare von Floyd kürzester Pfad-Algorithmus
  • Die Optimierung der Ordnung für die Kettenmatrixmultiplikation
  • Pseudopolynomische Zeitalgorithmen für die Teilmenge-Summe- und Rucksack- und Teilungsproblem-Probleme
  • Der dynamische Zeitverwerfen-Algorithmus, für die globale Entfernung zwischen zwei Zeitreihen zu schätzen
  • Der Selinger (a.k.a. System R) Algorithmus für die Verwandtschaftsdatenbank fragt Optimierung
  • Algorithmus von De Boor, um B-Spline-Kurven zu bewerten
  • Methode von Duckworth-Lewis, für das Problem aufzulösen, wenn Spiele des Krickets unterbrochen werden
  • Das Wertiterationsverfahren, um Entscheidungsprozesse von Markov zu lösen
  • Ein grafischer Bildrand im Anschluss an Auswahl-Methoden wie das "Magnet"-Auswahl-Werkzeug im Photogeschäft
  • Einige Methoden, um Zwischenraum-Terminplanungsprobleme zu beheben
  • Einige Methoden, um Wort zu beheben, wickeln Probleme
  • Einige Methoden, für das Handlungsreisender-Problem, irgendein genau (in der Exponentialzeit) oder ungefähr (z.B über die Bitonic-Tour) zu beheben
  • Rekursiv kleinste Quadratmethode
  • Schlagen Sie das Verfolgen in der Musik-Informationsgewinnung.
  • Anpassungsfähige Kritiker-Lehrstrategie für künstliche Nervennetze
  • Stereoalgorithmen, für das Ähnlichkeitsproblem zu beheben, in der Stereovision verwendet.
  • Das Naht-Schnitzen (zufriedenes bewusstes Image in der Größe anpassend)
  • Der Algorithmus von Ford des Öffentlichen Ausrufers, für die kürzeste Entfernung in einem Graphen zu finden.
  • Einige ungefähre Lösungsmethoden für das geradlinige Suchproblem.
  • Der Algorithmus von Kadane für das Maximale Subreihe-Problem.

Siehe auch

  • Gleichung des öffentlichen Ausrufers
  • Konvexität in der Volkswirtschaft
  • Teilen Sie und überwinden Sie Algorithmus
  • Gieriger Algorithmus
  • Entscheidungsprozess von Markov
  • Nichtkonvexität (Volkswirtschaft)
  • Stochastische Programmierung

Weiterführende Literatur

  • . Eine zugängliche Einführung in die dynamische Programmierung in der Volkswirtschaft. Die Verbindung enthält Beispielprogramme.
  • . Schließt eine umfassende Bibliografie der Literatur im Gebiet bis zum Jahr 1954 ein.
  • . Paperback-Ausgabe (2003) von Dover, internationale Standardbuchnummer 0-486-42809-5.
  • . In zwei Volumina.
  • . Besonders Seiten 323-69.
  • .
..
  • S. S. Sritharan, "Dynamische Programmierung Navier-schürt Gleichungen," in Systemen und Kontrollbriefen, Vol. 16, Nr. 4, 1991, Seiten 299-307.
.

Links


Wahnvorstellung / Wissenschaft und Gesundheit mit dem Schlüssel zu den Bibeln
Impressum & Datenschutz