Ja, und dann müssen wir natürlich auch zeigen,
dass sie für jeden Schleifendurchlauf gilt und dann müssen wir am Ende zeigen,
dass für die Terminierung daraus auch das gültige Resultat,
das ist unser Theorienvolk.
Wir machen jetzt erst den ersten und den dritten Schritt.
Der erste Schritt ist Initialisierung. Am Anfang ist die Menge,
er ist leer und die Menge N besteht aus dem
einen Knoten S, mit unseren Startknoten.
So, wenn die enge S leer ist,
dann müssen wir hier gar nicht zeigen,
dann gibt es ja hier gar keine Knoten.
Und für den Knoten klein ist, der in der Menge N ist,
naja, für den ist diese Aussage offensichtlich ja auch wahr.
Das heißt, wir wissen D von SS 0
und die Länge des kürzesten Fades ist ja
auch null
Ist diese Aussage auf alle Fälle korrekt.
Am Anfang ist das also alles richtig.
Und wir schauen uns auch mal schon das Ende an.
Mal angenommen, diese Wildschleife ist bis zum Ende durchgelaufen.
Alle Knoten sind dann nachher in der Menge S drin,
weil wir sie aus der,
wir haben sie ja hier aus der Warteschlange herausgenommen und in S hinzugefügt.
Die sind dann alle da drin. Deswegen ist S
der komplette Graf, alle Knoten drin.
Und wenn wir uns Teil 1 der Innenvariante anschauen,
dann sehen wir einfach, hier würde dann für SV stehen.
Also für alle Knoten V aus der Knotenmenge V ist DVV
gleich Delta SV. Also wir haben hier oben
das Resultat berechnet.
Es folgt direkt darauf
Okay, offensichtlich, wie immer,
ist nur der schwierige Teil, sich zu überlegen,
dass diese Eigenschaften auch erhalten bleiben während der Algorithmus tatsächlich läuft.
Okay, so, wie geht das? Wir sind jetzt
zu irgendeinem Zeitpunkt in dem Algorithmus. Wir wissen,
dass der Algorithmus bis zu dieser Iteration korrekt war.
Also für S kennen wir alle kürzesten Pfade,
für diese Nachbarschaft wissen wir, wir hätten die kürzesten Pfade berechnet.
Wenn sie denn direkt in die Menge S hineingehen,
wenn nicht, dann noch nicht.
Und für die anderen Knoten hier hinten wissen wir noch
gar nichts
So. So, jetzt schauen wir uns einen Knoten-U an,
dieser Knoten-U, den wir uns anschauen.
Das ist jetzt mal dieser hier, ja.
Das ist der mit minimalem D von U-Wert. Also das
ist der, den wir jetzt auswählen aus der Prioritätswateschlange.
Der hat also D von Us. Zu dem Zeitpunkt minimal. So,
dann holen wir den da raus und überlegen uns,
was jetzt für den gelten kann.
Mal angenommen, für den gilt D von U wäre unendlich.
Das ist jetzt ein spezieller Fall. D von U gleich
unendlich bedeutet ja, dass sich der Wert nie geändert hat.
Am Anfang ist die obere Schranke unendlich, so wird sie initialisiert.
Und diese obere Schranke, das haben wir schon gezeigt,
wird immer nur kleiner, während der Algorithmus läuft.
Bei der Relaxation können die nur kleiner werden.
Und wenn der immer noch unendlich ist, haben wir zu
keinem Zeitpunkt irgendeinen Pfad gefunden. Also es folgt,
es gibt keine Kante von V nach U,
die mit V-Element S, sodass das Delta SV kleiner wäre als unendlich.
Ja, es gibt für diesen Knoten gar keine Möglichkeit,
in dieses Netzwerk hineinzukommen, beziehungsweise nicht zu einem Knoten,
der erreichbar ist von dem Knoten S.
Und deswegen gilt auch Ja, dass es keinen Pfad überhaupt gibt,
um diesen Knoten U zu erreichen und der Wert ist dann korrekt.
Das ist einfach der Sonderfall,
dass unser Netzwerk gar nicht zusammenhängend ist und es gibt Bereiche von Knoten,
die wir gar nicht, von die wir gar nicht von dem Start noten S aus erreichen können.
Okay, das ist der langweilige Fall. Der interessante Fall
ist natürlich D von U ist kleiner als unendlich.
Der hat jetzt also irgendeinen realen Wert. Wir haben also
einen Pfad gefunden. U ist dann in der direkten Nachbarschaft
ändern, so wie das da gezeigt ist.
So, und es gibt jetzt einen kürzesten Pfad P,
der geht von S über X, Y bis U.
Wir wissen jetzt, das ist jetzt der tatsächliche kürzeste Pfad,
um zu diesem Knoten U zu gelangen oder einer davon
Der könnte natürlich hier langlaufen, könnte ja auch woanders langlaufen,
mal angenommen, der würde hier oben langlaufen, dann würde der hier,
wäre irgendwann mal bei einem Knoten X und so ist das in der richtigen Reihenfolge machen,
nein, bei einem Knoten Y und dann bei dem Knoten X und dann würde der hier reinverlaufen,
ja, der würde dann also so ein, diesen,
diesen Pfad hier beschreiten, ja, so wäre der,
da würde der hier langgehen.
Also das wäre jetzt der kürzeste Pfad,
der uns vom Staatsknoten zu U bringen würde.
Jetzt haben wir hier ein Szenario aufgemalt,
von dem wir uns fragen müssen,
ob das existieren kann, ob das geht,
weil wir sind ja von U aus nicht direkt in die Menge S hineingegangen,
sondern haben ja irgendwie einen anderen Pfad gewählt,
der uns dann irgendwie, oder der kürzeste Pfad wohl war.
Gut, mal angenommen, es gibt so einen Pfad,
dann wählen wir den Quoten Y so,
als den ersten Knoten, der nicht in S war.
Das habe ich so gemacht. Also hier, das ist,
ich bin jetzt in Pfad rückwärts gegangen und Y war quasi
der erste Knoten, der nicht in S war.
Wir wissen jetzt, dass D von U kleiner gleich D
von Y ist
Warum? Weil wir haben ja DVNU gewählt bei der,
weil es jetzt aus der Prioritätswarteschlange herausgenommen haben.
Und zu dem Zeitpunkt war Y auch in der Prioritätswarteschlange und dieses D von Y,
das muss größer sein als D von U oder mindestens mal größer gleich.
Weil sonst hätten wir ja Y genommen. Wir haben ja
über das Minimum genommen. Also insofern wissen wir,
dass das DVN Y hier größer gleich DVNU sein muss.
So, jetzt wissen wir aber für das Y
der kürzeste Pfad von Y hier zu S,
der geht direkt als nächstes in die Menge S hinein
Für den gilt diese Eigenschaft auf alle Fälle. Und nach
der Schleifen in Variante wissen wir, für den Knoten gilt,
dass das gleich Delta OMS von SY ist.
Und das ist ja der korrekte kürzeste Pfad für diesen Knoten.
Und deswegen ist das gleich dem Delta von SY. Weil,
wenn das ein kürzester Pfad ist, ist natürlich der Pfad
von S zu dem Y auch ein kürzester Pfad. So,
okay, weil das Y auf dem kürzesten Pfad von S
zu unserem Zielknoten liegt, wissen wir auch,
dass der kürzeste Pfad zur U länger ist,
als der zu Y. Und deswegen ist das kleiner gleich
Delta von SU
So, damit haben wir gezeigt, dass das D von U,
kleiner gleich Delta, SU ist. Und wir wissen ja schon,
dass das immer eine obere Schranke ist für das Delta,
also für die kürzesten Fahrtlänge. Und damit haben wir nachgewiesen,
dass das Delta SU kleiner gleich D von U,
kleiner gleich Delta SU ist, wegen der Eigenschaft der oberen Schranke.
Und wegen diesem Teil eins des Beweises. Und damit haben wir gezeigt,
dass wir für diesen Knoten U den korrekten Wert bestimmt haben.
So, jetzt müssen wir auch noch den zweiten Teil,
müssen wir auch noch zeigen.
Also diesen Teil. Und das muss man aber gar nicht
so explizit hinschreiben, wenn man sich überlegt,
dass wir als nächstes ja alle Kanten relaxieren,
die diesen Knoten verlassen
In die nächsten Bereiche des Graphen, dann ist klar,
dass danach auf alle Fälle der kürzeste Weg in S damit beschrieben wurde.
Okay, so, bevor wir diesen Beweis hier hinter uns lassen,
sollten wir auf alle Fälle nochmal einmal reinschauen,
wo wir eigentlich die Eigenschaft benötigen, dass die Kantengewichte immer positiv sind.
Könnte ja sein, dass es gar nicht so wichtig ist
oder dass man das irgendwie umgehen kann oder so.
Aber hier sieht man relativ schnell an dem Beweis,
dass es an sehr kritischen Stellen der Fall ist, dass man diese Eigenschaft benötigt.
Und zwar Hier zum Beispiel, ja,
wenn wir sagen, dass der kürzeste Pfad zu Y,
kleinergleich der Länge des kürzesten Pfades zu U ist,
gilt das natürlich nur, wenn diese Kante ein positives Gewicht hat.
Wenn die jetzt hier ein Gewicht von minus vier hätte zum Beispiel,
dann wäre ja das Delta SU,
kleiner als das Delta S zu dem Y. Das heißt,
dieser Schritt hier würde schon nicht gelten.
Wir könnten also nicht abschätzen, ob tatsächlich wir hier den kürzesten Pfad berechnen würden.
Und sie werden es in der Übungsaufgabe sehen,
wenn sie es noch nicht gesehen haben,
durch die Erinnerung, dass sie vielleicht noch mal gucken,
aber seit Montag gibt es einen neuen Übungshettel,
dass es tatsächlich auch nicht funktioniert.
Oder können Sie an einem Beispiel, das mal einmal ausprobieren
und dann sehen Sie, dass es tatsächlich nicht geht.
Okay, aber für positive Kantengewichte,
die ja der häufigste Fall in der Anwendung sind,
funktioniert der Diaxtra-Algorithmus hervorragend und wir können uns noch überlegen,
wie lange das alles dauert.
Wir haben hier eine, diese Wildschleife,
die natürlich N-Durchläufe hat,
also für jeden Knoten wird die einmal bearbeitet und da müssen
wir ja mit unserer Prioritätswarteschlange arbeiten und dann haben wir
diese Extractract-Min-Operation und wir haben so Relax und die Creeze-Operation.
Können da
nochmal hingehen
Also hier haben wir die Wildschleife, die hat N-Durchläufe,
dann kennen wir schon die Konstruktion, eine Wildschleife über alle Knoten,
da drin, eine Vorschleife über die Additizenzliste bedeutet immer,
dass das, was sie innen drin ist,
für jede Kante gemacht wird oder, naja,
für jede Kante maximal zweimal, hängt davon ab,
ob gerichtet oder umgerichtet.
Das heißt, diese Relax-Operation, wo sich das Decrease verbirgt,
wo wir in der Prioritätswarteschlange diesen Knoten neu einsortieren müssen,
wird also M mal, also O von M mal,
M ist die Anzahl der Kanten im Grafen,
durchlaufen.
Die X-Tract-Min-Operation wird in jeder Waldschleife einmal durchlaufen,
also für jeden Knoten einmal
Also NX-Track-Min-Operation und M, die Crease-Operation auf der Prioritätswarteschlange und der ganze Rest
in diesem Algorithmus geht in Linearzeit, sieht man eigentlich.
Also hier diese Initialisierung betrachtet also jeden Knoten irgendwie einmal und
hier diese Schlangen initialisieren, das geht auch in Linearzeit.
Achso, für die Operation ist es natürlich jetzt nicht so ganz so trivial.
Also hier wird die Prioritätswarteschlange aufgebaut. Das haben wir tatsächlich bewiesen,
wenn sie sich an den Heepsort erinnern, haben wir überlegt,
wie lange denn das Bild maximp gedauert hat.
Und da haben wir gesehen, das dauert, geht in Linearzeit.
Und das ist genau das, was ja hier passiert.
Wir müssen quasi für alle Knoten
diesen Heep bauen
Streng genommen ist hier natürlich ein bisschen einfacher,
weil die haben natürlich alle noch das Gewicht, also den D-Wert unendlich.
Also insofern gibt es da auch nicht so viel zu sortieren. Okay, so,
Waldschleife hier wird en mal durchlaufen und das hat alles dann,
sowas hat konstante Zeit und auch diese Rechenoperation auch konstante Zeit.
Also das einzige, worauf es also ankommt, ist,
wie lange brauchen wir für unsere Prioritätswarteschlange?
Okay, so, also wir müssen die implementieren.
Wir können ein Hieb verwenden. Beim Hieb wissen wir,
die Extract-Min-Operation braucht Lock-and-Side im Okalkül, um Knoten zu elementieren, herauszunehmen.
Wir nehmen den ja raus von der Spitze des Hiebs,
wir nehmen das Element, was am Ende des Hieb stand,
setzen sie nach oben, machen das hipify und dann haben wir gesehen,
das dauert Login-Schritte.
Die Quizkey kann im ungünstigsten Fall auch Login-Schritte bedeuten. So,
wenn wir das dann zusammenfügen, dann sehen wir,
dass der da extra, also O von N-Login,
für die Extract-Min-Operation, O von M-Login,
für die die Quizkey-Operation hat, macht O von M-Login.
Und M war die Anzahl, ja, kannten. Ja,
und der Gress war O von N plus M,
das ist natürlich viel kleiner als diese Schranke hier.
Das galt lange als das absolute Optimum zur Berechnung kürzester Wege
Hat man gedacht, okay, das ist wahrscheinlich, dass die Zeit,
die man einfach braucht, ja, um kürzeste Wege zubrechnen in Grafen.
Also eins, zwei Kanten und dann ein Lok von unseren
Knoten als so ein Faktor da dran. Man wusste,
man einfach als untere Schranke natürlich nur die triviale untere Schranke O von V plus E,
ne, für Grafen, aber da ist jetzt also so ein Delta,
hier hat man ja so einen Log-End-Faktor,
man ging aber lange davon aus,
dass es besser irgendwie nicht geht.
Und man kennt einfach die untere Schranke nicht. Das ist
halt für vielen Problemen so ein Candy untere Schranke nicht.
Und ja, bis dann jemand gekommen ist und gezeigt hat,
dass es besser geht, dass man tatsächlich die göse müssen Wege schneller berechnen kann,
als diese M-Blog-End-Site.
Und das Faszinierende dabei ist, er hat den Diextra-Algorithmus gar nicht angefasst.
Er bin jetzt nach wie vor den Diextra-Algorithmus und hat
lediglich gesagt, ich nehme eine andere Prioritätswarteschlange.
Er hat dann eine andere Prioritätswarteschlange entworfen.
Das sind die sogenannten Fibonacci-Heeps, die machen wir hier nicht.
Die sind ein bisschen komplizierter. Da braucht man ein bis zwei
Vorlesungen, um zu erklären, wie die genau funktionieren.
Und die haben die faszinierende Eigenschaft, dass die Extract-Mind-Operation
nach wie vor Log-End-Site benötigen, also so wie vorher.
Das ändert sich nicht. Aber diese die Krease-Key-Operation,
die ja am häufigsten ausgeführt wird,
die wird ja für jede Kante ausgeführt,
die läuft in konstanter Zeit
Die läuft auch nicht wirklich im Worst-Case konstanter Zeit.
Deswegen steht hier amortisiert, sondern man kann zeigen,
wenn man diese Decrease-Key-Operation auf alle Kanten ausgeführt hat,
dann wird sie insgesamt nur O von M-Zeit benötigen.
Und wenn man das dann dividiert,
kommt man auf O von 1-Zeit pro Kante. So,
und wenn man das jetzt wieder hier oben einsetzt,
dann kommt man für den Diextra zu einer neuen Laufzeit,
nämlich zu O von N-Log N plus M.
Und das ist besser. Das ist wesentlich besser,
weil die Anzahl der Kanten kann ja quadratisch sein in der
Anzahl der Knoten, wenn wir ein sehr dichtes Netzwerk haben.
Für ein dünnes Netzwerk, also wenn jeder,
also für so eine Stadtkarte wird das keine Rolle spielen,
da hat jeder Knoten vielleicht vier,
fünf oder vielleicht nochmal acht
Kannten, da wäre die Anzahl erkannten,
linear in der Anzahl der Knoten, da würde es keinen Unterschied machen,
aber für ein sehr dichtes Netzwerk ist das deutlich schneller als M Login.
Ja, da haben wir gesagt, okay, jetzt wissen wir ja,
wie es ist und wenn wir jetzt also ein großes,
dichtes Netzwerk haben,
dann muss man eben Fibonacci-Hieb verwenden und es ist aber dummerweise so,
wie häufig dieser Hieb hier hat sehr große Konstanten.
Also das heißt, für kleine Grafen lohnt sich das auf
alle Fälle nicht. Man müsste schon also einen sehr großen,
sehr dichten Grafen haben,
dass man
einen Unterschied sieht
Aber selbst dann ist es noch die Frage, ist das jetzt wirklich besser?
Man würde sagen, ja, klar, ist das besser.
O von N-Log M plus M und O von M-Log N.
Aber um zu bewerten, ob es besser ist,
müssten wir eigentlich etwas anderes wissen. Wir müssten nämlich wissen,
dass der Deikster mit dem normalen Hieb die Laufzeit von Omega M-Log ändert.
Also, dass er mindestens M-Log N-Zeit braucht. Das steht hier
aber nicht. Da steht ja nur die obere Schranke,
die wir kennen, ist O von M-Log N. Das heißt,
um wirklich zu sagen, das ist das bessere Verfahren,
müsste man zeigen,
hier gibt es eine untere Schranke und der Algorithmus braucht
mindestens so viel Zeit
Das ist eine Beobachtung von Donald Knuz,
der dann bei der Diskussion des Figunaccieves gesagt hat,
ja, der ist ganz toll,
aber wir wissen nicht, ob er wirklich besser ist.
Wir haben nur eine bessere obere Schranke.
Wir haben eine bessere obere Schranke, aber wir wissen nicht,
ob der normale Hieb tatsächlich so lange benötigt.
Insofern ist es ein bisschen zweifelhaft, ob man das wirklich benutzen will.
Das ist etwas sehr Hochspezielles. Und der normale Diextra läuft
mit einem normalen Hieb und das ist für nahezu alle Anwendungen
auch effizient genug.
Okay,
so, das war's zum Diextra-Algorithmus
So, man könnte jetzt noch sehr, sehr lange über Graph-Algorithmen reden.
Die Zeit haben wir leider hier nicht mehr. Ich möchte
Ihnen wenigstens noch ein paar Pointer geben, auf andere Probleme,
die man sich anschauen kann, die ganz wichtig sind.
Das erste Problem ist, dass All-Pairs short this Path-Problem.
Also kürzeste Wege, aber jetzt nicht von einem Knoten zu
allen anderen, sondern zwischen allen Knotenpaaren gleichzeitig.
Und da gibt es, ja, da kann man natürlich
en mal den Dikestra-Algorithmus anwenden. Hätte man also en-mal,
ja, er steht noch N plus M,
aber er könnte auch einmal M-Log-End,
weil so en-mal die Laufzeit vom Dikestra-Algorithmus,
machen mit negativen Kantengewichten
Den Bellmann-Ford-Algorithmus. Da gibt es aber tatsächlich einen besseren Algorithmus,
den, wenn sie irgendwann mal über dieses Problem stolpern und
wenn sie nicht Algorithmik hören, sollten sie diesen Namen kennen.
Das ist der Floyd Warshall-Algorithmus. Ja, wenn sie kürzeste
Wege zwischen allen Knotenpaaren berechnen, ist das die Methode der Wahl.
Und der Algorithmus funktioniert, kurz gesagt,
auf einer ganz einfachen, rekursiven Betrachtung.
Der sagt nämlich, der kürzeste Pfad von I nach J,
der kann entweder über den Knoten K laufen oder nicht.
Wenn er über den Knoten K läuft,
dann ist es der kürzeste Pfad von I nach
Ohne dass er den Knoten K verwendet,
weil kein Knoten kommt ja doppelt vor auf den kürzesten Pfad.
Plus die Länge des kürzesten Pfades von K nach J.
Wenn er den Knoten K verwendet, wenn nicht,
dann ist es der gleiche kürzeste Pfad ohne Knoten
K. Das heißt, man kommt für die Länge des kürzesten
Pfades unter Verwendung aller Knoten von 1 bis zur Nummer K.
Also die werden echt durchnummeriert. In irgendeiner beliebigen Reihenfolge kommt man zu dem Schluss,
entweder ist das die Kantenlänge, wenn K gleich Null ist,
also wenn ich keinen anderen Knoten verwenden kann,
außer I und J. Und ansonsten ist es eben entweder der kürzeste Pfad von I nach J der Binknoten K nicht verwendet.
Oder die Länge des kürzesten Pfades von I nach K,
ohne Knoten K, plus die Länge des kürzesten Pfades von K nach J, ohne Knoten K.
Und diese rekusive Definition kann man in einer dreifachen Vorschleife
einfach ausrechnen. Zwei Vorschleifen brauchen sie für alle Knotenpaare,
I und J und die brauchen sie,
um das Karl langsam hochzuzählen von eins bis Ende.
Dann können sie rekusiv diese DEJ OMK berechnen. Geht in
Nhof 3 und ist einfach aufgrund der Kompaktheit und der Einfachheit
die Methode der Wahl schlechthin für den Floyd Warshall.
Also für die Berechnung kürzester Pfade, zwischen allen Knotenpaaren,
wenn man das Problem hat
Das ist nicht das effizienteste Verfahren bezüglich des O-Kalküls,
aber es ist eins, was in der Praxis einfach viel verwendet ist.
So, hier ist noch ein Problem,
da werde ich gleich über zwei, drei Folien springen.
Da möchte ich auch nur, dass sie das Problem kennen.
Es gibt das Problem des maximalen Flusses. Das tritt im betriebswirtschaftlichen Aufwendung,
Anwendungen, viel auf, in der Optimierung tritt das auf.
Und das Problem ist wie folgt,
sie haben also ein Netzwerk, also ein Graf,
sie haben einen definierten Staat und einen Zielknoten.
An den Kanten haben sie Kapazitäten,
das sind jetzt keine Längen,
sondern Kapazitäten
Sie können sich das Ganze so vorstellen wie so ein Rohr-Netzwerk
und das sind die Rohrdurchmesser. Also hier können irgendwie,
haben sie, können irgendwie 16 Einheiten von S
nach V1 pro Stunde fließen oder was auch immer.
So, und dann haben sie dieses Netzwerk und sie wollen wissen,
wie viel Einheiten bekommen sie von der Quelle S zu Senke Tee.
Ja, so ein klassisches Logistikproblem. Sie können sich auch vorstellen,
das sind ihre Lager und das sind ihre LKWs,
die da langfahren oder sie haben Stromverteilungsprobleme und das
sind ihre Stromleitungen oder die Kapazitäten von Netzwerk, Kanten,
Telefonleitung, was auch immer.
Ja, und sie wollen einfach die Kapazität voll ausnutzen und
so viel wie möglich von S nach Tee zu pumpen,
ja,
oder zu transportieren
Okay, das ist das Flussproblem. Das Flussproblem ist
sehr gut verstanden. Es ist auch gut gelöst. Es
gibt Algorithmen, die das machen. Die sind aufwendiger,
die sind so eher in der Größenordnung von Nhoch 3,
um das Problem dann zu lösen.
Aber man kann das gut lösen und kriegt das raus.
Und wir machen das jetzt hier nicht. Das wäre sowieso
nur eine kurze Einführung gewesen, wie das Ganze funktioniert.
Und dann, wenn es sich interessiert,
entweder sie lesen es oder sie gehen in die Algorithmikvorlesung,
da gucken uns die ganzen Algorithmen für Flüsse an.
So, das nächste Problem, was für Grafen wichtig ist,
und was meiner Meinung nach fast eins der,
neben den kürzesten Faden, eines der wichtigsten Probleme ist auf Grafen,
sind Matching-Probleme
Matching-Probleme sind so Zuordnungsprobleme. Sie haben zwei so Mengen,
also zum Beispiel hier die Menge L,
die Menge R. Und sie suchen eine Zuordnung von Knoten von L,
so Knoten aus R, sodass das Gewicht über diese,
was sie hier brauchen, über diese Kanten,
die haben da so ein Gewicht, minimal ist.
So Zuordnungsprobleme gibt es ohne Ende. Ich nenne es auch immer
das Tanzschulproblem mit den Jungs und den Mädchen und das ist der Beliebtheitswert,
wie sehr sie sich mögen und das müssen sie maximieren.
Sie können alles Mögliche zuordnen, typischerweise ordnet man auch Aufgaben zu,
um meinetwegen Fabriken zu sie ordnen, Produktionsprozesse,
speziellen Maschinen zu, es gibt so viele Dinge,
die man zuordnen muss in der Informatik,
die fast immer zu solchen Matching-Problemen führen.
Also wenn man sie optimiert. Das ist das Einfachste,
die einfachste Art des Matchings hier,
das ist bipartietes Matching, also mit zwei Mengen,
kann natürlich auch in den beliebigen Grafen solche Matchings berechnen,
dann wird es richtig schwer.
Wenn man keine Gewichte hat, dann ist das effizient lösbar.
Wenn man Gewichte hat, wird es schwierig. Das ist lösbar,
ja, es ist auch gut lösbar, es gibt einen schönen Algorithmus dafür,
die ungarische Methode lernt man auch in Algorithmik.
Ist allerdings auch aufwendig zu erklären. Das erklärt man nicht
mal eben, wie das funktioniert. Es ist schwierig.
Kann man aber lösen. Okay. So,
und dann gibt es eben auch sehr, sehr viele Grafprobleme,
über die wir ja nicht reden, die wir nicht effizient lösen können.
Ja, wo wir einfach keinen Algorithmus haben.
Und hier sind so ein paar Beispiele und die zeigen einfach,
dass Probleme, die uns relativ einfach erscheinen, manchmal schwer lösbar sind.
Also, der Hamilton Path ist ein ganz berühmtes Beispiel der Hamilton Cycle,
heißt der auch, je nachdem, ob man einen Vater oder einen Zyklus sucht.
Also gibt es einen einfachen Zyklus der Länge,
Also einfach bedeutet, jeder Knoten darf nur einmal besucht werden.
Also können sie in einem Grafen quasi von einem Knoten starten,
einen Rundweg beschreiben, wo sie jeden Knoten genau einmal besuchen.
Das erscheint nach einem Problem, was irgendwie lösbar ist.
Da muss man einfach nur ein bisschen ausprobieren, wo man langläuft.
Und dann wird man so einen Weg schon finden. Das ist so die Frage,
die sich vielleicht ein Touristenführer bei dem Stadtrundwanderweg überlegt,
wo sollte man langlaufen, wie man in allen Sehenswürdigkeiten einmal vorbeikommt,
aber bitte auch nur einmal.
Dann möchte man natürlich, wenn man das jetzt noch optimiert,
vielleicht die Weglänge auch noch minimieren, aber schon die Frage,
ob dieser Pfad existiert oder nicht,
gehört zu den schwierigen Problemen
Ist nichts oder heute, ist kein Verfahren bekannt,
was das in polynomäler Zeit tun kann.
Die Subgraffrage, die gibt es unheimlich viel in der Praxis.
Also ich habe einen Grafen, ich habe noch einen Grafen.
Ich frage, kommt der da drin vor, in dem anderen Grafen?
Ist ein Problem, was wir nicht effizient lösen können.
Grafisomorphie ist ein ganz spezielles Problem. Also wir haben zwei Grafen,
wollen wissen, sind die gleich. Ist ganz schwierig,
weil wir es nicht genau wissen, wo das hingehört.
Ob das schwer ist, aber es gibt keinen effizienten Algorithmus,
aber wir wissen nicht so ganz genau,
wie schwierig das Problem wirklich ist.
Von den anderen haben wir ein Gefühl. Das Klickproblem erscheint
auch extrem einfach, das werden wir uns noch anschauen,
gibt es einen vollständig verbundenen Teilgraf mit Karknoten.
Also können wir aus einem großen Grafen Karknoten auswählen,
dann ist jeder
mit jedem verbunden
Das ist Kriegproblem. Oder drei Färbbarkeit, gibt es eine Funktion,
die jedem Knoten eine eine von drei Farben zuordnet,
0-1 oder 2, mit der Eigenschaft,
dass zwei Knoten, die mit einer Kante verbunden sind,
nicht mit der gleichen Farbe gefärbt sind.
Sind so Beispiele aus der Praxis, die vorkommen,
also über diese Probleme stolpert man, wenn man Probleme modelliert,
die alle nicht so leicht zu lösen sind.
Okay, einiges davon werden wir noch mal wiedersehen,
wenn wir uns mit dem Bearbeiten schwieriger Probleme auseinandersetzen.
Aber das werden wir jetzt noch nicht tun.
Wir werden erst noch eine andere Strategie, algorithmische Strategie kennenlernen,
bevor wir das machen. So,
die Hinweise zur Klausur, die Sie ja noch mal sehen,
die habe ich in der letzten Vorlesung genannt,
muss ich das hier noch mal tun?
Waren Sie alle da
Ja, sehr gut. Dann können wir voranschreiten.
So, und werden uns jetzt mit dynamischer Programmierung beschäftigen.
Das ist das nächste Kapitel für diese und die nächste Vorlesung.
Und wir machen wieder einen gedanklichen Schritt, aber wir werden etwas anders machen als bisher.
Bisher haben wir uns immer von einem Problem zu einem Algorithmus
vorgearbeitet, der dann das Problem gelöst hat.
Wir werden jetzt einen Wechsel machen. Sowas kann man natürlich nicht ewig betreiben,
weil es gibt immer neue Probleme und es ist einfach der Punkt,
wo man mal darüber nachdenken muss, was sind die richtigen Strategien,
die man anwenden kann, wenn man mit einem neuen Problem konfrontiert wird.
Und eine Strategie, naja, wir gehen natürlich davon aus,
wir haben Probleme, die schwierig sind,
also die einfachen, die lösen sie so,
aber jetzt kommen sie an die schwierigen,
jetzt kommen sie an die,
wo sie nicht wissen,
gibt es eine gute Lösung oder gibt es keine und dafür
brauchen wir Konzepte,
wie wir
diese Probleme angehen können
So, und eines dieser Konzepte ist die dynamische Programmierung.
Und wenn sie quasi mit einem Problem konfrontiert sind,
was so ein kombinatorisch Optimierungscharakter hat, dann ist es
immer gut zu prüfen, ob diese Technik anwendbar ist.
Sollte man immer ausschließen, bevor man sich mit irgendwelchen Heuristiken
abgibt und versucht, Probleme so Larifari zu lösen.
So, okay, wie funktioniert das Ganze? Ja,
hier ist ein bisschen viel Text drauf, aber es ist erstmal
nur um den Einstieg in das Problem, in diese Strategie zu gekommen.
Die Lösungsstrategie, die wir häufig verwenden, sind so Zerlegungsstrategien. Wir sagen,
oh Mann, Schwieriges Problem, große Eingabe,
vielleicht können wir das Problem lösen,
indem wir die Eingabe zerlegen, in kleiner Teile,
dann ist das Problem vielleicht immer noch schwer,
aber wir haben eine kleine Eingabe,
da können wir vielleicht relativ gut das Problem noch herausfinden,
wie es geht.
Und dann brauchen wir eine Strategie,
um die Lösung zu kombinieren, zu einer großen Lösung.
Das ist quasi so eine Strategie, die ganz typisch ist.
Und die führt uns zu rekusiven Algorithmen. Also wir zwingen
die Eingabe des Problems in ein oder mehrere Teilprobleme,
wir wenden das Prinzip rekusiv an, wir teilen,
teilen, teilen,
teilen,
dann haben wir irgendwie ganz kleine Probleme und für die kleinen Probleme
berechnen wir dann die Lösung,
das ist in der Regel einfach
Und dann brauchen wir eine Möglichkeit,
eine Lösung des Gesamtproblems aus den Lösungen der Teilprobleme zu erzeugen.
Und dann können wir das rekrusiv wieder anwenden und dann haben wir die Gesamtlösung.
So, die Implementierung dieses Konzepts haben wir schon ganz häufig gesehen,
das ist das Devide-in-Connka-Prinzip, haben wir zum Beispiel gesehen, beim Sortieren.
Im Merch-Sort, Quick-Sort sind Devide-in-Connka-Prinzipien. Wir teilen die Datenmenge,
wir teilen sie, wir teilen sie, wir teilen sie.
Irgendwann haben wir nur noch ein oder zwei Datenelemente,
die können wir sortieren. Und dann haben wir eine Strategie,
das Ganze zu einer Lösung zusammenzufügen. Ja,
bei Merch-Sort der Merch-Schritt, der dann aus zwei sortierten Liste
eine sortierte Liste machen,
beim Quick-Sort ist
ein bisschen anders
Und da sorgt man vorher dafür, dass dieses Zusammenfügen nachher trivial ist.
Okay, also das ist die White and Conquer,
Teilung in zwei voneinander unabhängig zu lösen, Teilprobleme,
rekrusive Lösung der Teilprobleme zusammenfügen.
Okay, haben wir schon gesehen. Es ist eigentlich nicht
so kompliziert. Nur manchmal ist die Welt nicht so einfach,
dass wir mit so einem Divide and Conquer klarkommen.
Also, dass wir so eine klare Lösung des Problems in Zwei-Teilprobleme haben.
Und dann wird es ein bisschen schwieriger. Also,
wenn wir jetzt sagen, wir haben jetzt nicht diese Aufteilung in zwei Probleme,
sondern in mehrere Probleme, Dann können wir immer noch das rekursive Schema anwenden.
Wir starten das dann für das Teilproblem, für das,
für das, für das.
Und dann fügen wir die ganzen Ergebnisse wieder zusammen.
Und wir sind eigentlich auch durch die Tür. Allerdings haben
wir hier das Problem, wenn man da genau hinschaut,
haben wir relativ schnell hohe Laufzeiten.
Ich werde ihnen gleich ein paar Beispiele zeigen,
wo sie zu unheimlich hohen Laufzeiten kommen,
wenn sie einfach nur rekursiv das Problem zerlegen.
Ein Grund für diese hohen Laufzeiten kann sein,
dass die Teilprobleme immer wieder lösen.
Sie teilen das irgendwie auf und kommen immer wieder zu
den gleichen Teilproblemen und brechen immer wieder die gleichen Lösungen
Und fügen das dann nachher immer wieder zusammen. Da haben sie exponentiell viel Laufzeit verbraten,
um immer das Gleiche zu tun. Wenn sie eine solche Problematik haben,
dann ist die Lösung natürlich immer die zu sagen,
gut, ein Teilproblem, was ich schon gelöst habe,
dann merke ich mir einfach, das Ergebnis,
speichere das irgendwo und hole es wieder hervor,
wenn ich das Teilproblem nochmal habe.
Da muss ich den rekursiven Teil eben nicht nochmal ausrechnen. Immer,
wenn sie das machen, dieser Schritt wird als Memoisation,
das ist irgendwie im Deutschen ein ziemlich doofes Wort,
also als das Merken von Teilergebnissen genannt,
das ist noch keine dynamische Programmier,
das ist erstmal nur das Merken von Teilergebnissen,
immer wenn sie diese Strategie verfolgen,
kommen sie zu dynamischen Programmierungen
So, was heißen dynamische Programmierungen? Wahrscheinlich denken sie jetzt alle,
wir sind wieder in SE 1, 2, 3 oder so.
Sind wir nicht. Das hat mit Programmierung relativ wenig zu tun.
Das ist ein rein historischer Begriff. Das heißt nur deswegen Programmierung,
weil wir in systematischer Art und Weise diese Tabellen speichern,
in denen wir die Informationen verwalfen.
Daher kommt der Name. So,
wir haben vier Schritte in der dynamischen Programmierung,
die werden wir an zwei Beispielen mal durchgehen gleich.
Also wie vier Schritte sind die folgenden? Charakterisat, Charakterisierung
des Problems. Wir müssen erst mal das Problem anschauen,
verstehen, Wie können wir das Problem zerlegen?
Wie können wir Teilprobleme identifizieren und wie können wir daraus neue Probleme erzeugen?
Das führt uns in der Regel zu einer rekursiven Lösung und zu einem rekursiven Algorithmus.
Also wir definieren den Wert der optimalen Lösung rekursiv.
Was wir sagen, das Optimum können wir berechnen aus dem Optimum,
um dieser Teilprobleme in der folgenden Art und Weise.
Dann haben wir eine rekursive Beschreibung. Das können wir auch
in einen Algorithmus umsetzen. Dann haben wir den Algorithmus,
der das Problem löst. Dieser Algorithmus ist ein Top-Down-Einsatz,
der heißt Top-Down, weil der rekursive Algorithmus startet mit
der gesamten Datenmenge
Der teilt auf, führt die Rekusiven Aufrufe durch,
macht wieder rekusive Aufrufe, wieder rekusive Aufrufe und teilt und teilt und teilt
und lebt sozusagen von der gesamten Datenmenge zu immer kleineren Anteilen.
Deswegen ist es top down. Was wir auch tun können,
ist, wir können die Berechnungsreihenfolge jetzt umkehren.
Wir machen eine Bottom-Up-Ansatz. Wir fängen also an bei kleinen Teil-Problemen,
berechnen die Resultate und setzen daraus die Lösungen zusammen.
Das ist quasi der nächste Schritt. Wir wandeln also das Top-Down,
den Top-Down-Ansatz in einem Bottom-Up-Ansatz um,
wir drehen das Ganze um,
speichern die entsprechenden Teillösungen und konstruieren dann daraus
die optimale Gesamtlösung
So werden wir vorgehen. Okay, das Konzept wird häufig genutzt zur Optimierung,
insbesondere dann, wenn wir diese entsprechende Struktur haben.
Die brauchen wir natürlich, damit wir es anwenden können.
Es ist sehr mächtig, also wir können ziemlich viele Probleme damit lösen.
Und das habe ich eben schon mal gesagt,
also wenn man mit einem neuen Optimierungsproblem konfrontiert ist, sollte man immer testen,
ob man das mit dynamischer Programmierung in den Griff bekommen kann.
So, hier ist mein erstes Beispiel.
Ist aus dem Kommen, können Sie da auch nachlesen.
Das ist ein ganz einfaches Beispiel.
Wir haben zwei Montagebänder. Das ist eine Montageband,
das ist das andere Montageband hier
Und auf diesem Montageband wird irgendwas produziert. Keine Ahnung,
Autos oder so, ja. Und dafür brauchen sie verschiedene Schritte
. Das sind die Stationen hier. S1, 1,
1 ist 1, 2. Also dann wird es immer so,
wie hier wird, was weiß ich,
das Chassi zusammengeschweißt und dann haben wir wieder Motor eingesetzt und so weiter und so weiter.
Und hier ist das Auto fertig. So,
sie haben jetzt zwei solche Montagestraßen, ja,
und wo sie diese Operation machen können,
die sind zu unterschiedlichen Zeiten mal gebaut worden, keine Ahnung.
Und da dauern halt diese Schritte unterschiedlich lang. Also bei
dem Montageband 1 dauert der Schritt 1 die Zeit A1,
1, ne? Also, was weiß ich, sagen,
dauert es drei Stunden und hier dauert es vielleicht nur zwei Stunden.
Hier dauert der Schritt vielleicht fünf Stunden,
hier dauert ja vielleicht sechs Stunden aus irgendeinem Grund.
Also die Zeiten sind unterschiedlich auf diesen beiden, auf diesen beiden Montagebändern.
Wenn sie jetzt, also sie können jetzt das eine Band nutzen,
dann würden sie also mit dem, mit dem Autoschassi hier so reinfahren.
Das dauert eine gewisse Zeit,
dann kommt diese ganze Montage und dann fährt das hier aus
dem Werk wieder raus und dann ist das Auto fertig und alles ist gut und sie haben eine gewisse Zeit benötigt.
Die Zeit, die sie dann brauchen,
ist die Zeit E1 plus A11 plus 1,
2 A1, 3 und so weiter bis hierhin plus X1
und dann ist das Auto fertig
Und hier unten können sie natürlich das Gleiche machen. So,
dann kommen sie hier und haben sie eine andere Zeit benötigt.
So, jetzt kommt der Sonderauftrag. Keine Ahnung,
Frau Merkel braucht ein neues Auto, der ganze Montageprozess wird durcheinander geworfen,
das Auto muss so schnell wie möglich hergestellt werden.
Und dann Nutzung beider Bänder. Sie haben jetzt die Möglichkeit zu sagen,
okay, dann gehe ich halt hier oben lang und irgendwann
ist vielleicht das Montageband 2 schneller als eins,
dann nehme ich einen Transfer in Kauf.
Ich transportiere, das schaffe ich jetzt hier runter,
das kostet mich T-1, 2 Zeiteinheiten, dann bin ich hier.
Und dann mache ich hier unten weiter und irgendwann
nehme ich vielleicht nochmal einen Transfer in Kauf,
gehe wieder hier hoch und baue das Auto hier oben
zur Endung weiter und zu rum
Und die Frage ist jetzt, was ist die beste Strategie?
Also wie kommen wir am schnellsten mit unserem Auto,
mit der Autoproduktion von dem Chassis hier vorne zu dem Endprodukt?
So, wenn wir das Problem erstmal aus der Ferne betrachten,
dann sollte ihnen auffallen,
dass sie das Problem wahrscheinlich als ein kürzester Pfadeproblem modellieren können.
Die haben wir bis eben gerade noch gesprochen.
Das geht natürlich auch. Man könnte sagen, ja,
können wir jetzt Küste Pfadeproblem modellieren?
Kann man da mit einem Diaxtra-Algorithmus lösen?
Das kann man
alles tun
Aber wir werden das jetzt natürlich nicht so machen,
weil wir benutzen dieses sehr einfache Beispiel als eine Einführung eben in die dynamische Programmierung.
Deswegen machen wir es jetzt mit dynamischer Programmierung.
Aber an der Stelle möchte ich nur schon mal betonen,
wenn man es andersherum sieht,
wenn ich ihnen zuerst die dynamische Programmierung beigebracht hätte und wir hätten dann überkürzeste Wege nachgedacht,
dann hätten sie sofort gesagt, oh ja, kürzeste Wege,
ist ja ein dynamischer Programmierung.
Denn der Deikstra-Algorithmus, also die Art und Weise,
wie wir kürzeste Wege ausrechnen,
ist im Prinzip auch eine Art und Weise dynamische Programmierung umzusetzen.
Also es ist sehr verwandt,
die beiden Techniken
Okay, so, was nicht heißt, dass wir alle Probleme,
die wir mit dynamischer Programmierung lösen können,
mit kürzester Wege lösen können, das mitnichten, so.
Also das zweite Beispiel, was wir sehen,
werden wir nicht mit kürzesten Wegen lösen können. Ja,
aber jetzt auf der Hunde, das ist nur ein Podcast,
wenn man Tatum begleitet, dann wird alles.
Klar, klar. Das ist ein völlig, völlig aus
der Luft gegrissenes, konstruiertes Beispiel. Ja, also keine
Frage. Also es ist nicht praxisnah an dieser Stelle. Okay,
gut, also das ist die Eingabe,
die habe ich Ihnen gerade erklärt, die zwei Montagebänder,
die Stationen für die Stationen, die Montagezeiten,
einfach Ausfahrtzeiten und die Zeiten für diese Transfers,
für diese Montagebandwechsel hier.
Das ist alles gegeben und wir suchen jetzt also die
schnellstmögliche Montage
Unterverwendung bei der Bänder
So, als erstes mal die Frage, wie groß ist der Lösungsraum?
Wie groß ist, wie viele Möglichkeiten gibt es?
Vielleicht gibt es so wenig Möglichkeiten, zählen wir alle auf, sind wir fertig.
Müssen wir nur das Beste auswählen. Da sieht man ganz schnell,
das wird es wohl nicht sein,
weil wir können offensichtlich für jeden Montageschritt,
von dem wir enden, viele haben, uns entscheiden,
ob wir die Station des Bandes 1 oder des Bandes 2 nehmen.
Das heißt, für Endschritte haben wir jeweils eine Entscheidungsmöglichkeit zwischen
den beiden Wändern und dementsprechend haben wir zwei hochendviele Möglichkeiten,
wie wir hier durch dieses Netzwerk durchlaufen und zwar auch enden.
Viele werden wir nicht alle ausprobieren.
Das wird
zu langsam sein
Okay, es lohnt sich also über ein effizienteres Verfahren nachzudenken.
Jetzt überlegen wir, gibt es die Struktur einer optimalen,
wie sieht die Struktur einer optimalen Lösung aus?
Also können wir das Problem in Teilprobleme zerlegen, die wir dann lösen können,
sodass wir dann von dem Teilproblem zu dem Gesamtproblem gehen?
Okay, was machen wir? Wir betrachten mal die schnellste Fahrt,
die wir hinbekommen zu einer Station auf der,
an der Stelle J. Also sagen wir mal vier zum Beispiel.
Also wir wollen jetzt zu dieser Station hin und fragen uns,
was ist der schnellste Weg, um hier hinzukommen an diese Station?
Naja, und wir wissen, Also wenn wir hierhin kommen
, dann haben wir vorher den Schritt drei gemacht.
Wir sind Schritt drei, den können wir entweder auf dem Band 1
oder auf dem Band zwei gemacht haben. Das heißt,
es gibt nur zwei Möglichkeiten, hier hin zu belangen,
entweder über diese Station S13 oder über die Station S2, drei.
Beides Mal komme ich da in diese Position.
Und offensichtlich ist die schnellste Zeit, um hierhin zu kommen,
die schnellste Zeit, um zu der Station drei zu kommen,
entweder auf Band eins und bei zwei.
Und dann sozusagen hier den nächsten Schritt zu machen.
Wenn ich das Band wechseln muss, muss ich natürlich die Transferzeit noch berücksichtigen.
Das ist eigentlich schon alles. Das heißt,
wenn ich ganz am Anfang bin, ist die schnellste Zeit einfach die Zeit,
ja, die ich brauche, um diese Einführungszeit,
ist eh hier, da, um hier hinzukommen.
Und später gilt dann entweder wir,
wir betrachten jetzt das Band 1 im Moment,
entweder wir fahren von S1, J minus 1,
direkt zu S1 J. Wir bleiben also auf dem Band 1,
ja, da müssen wir nur die Montagezeit,
die nächste berücksichtigen.
Oder Möglichkeit 2, das Chassis wechselt das Band und kommt
von S2, J minus 1 zu S1 J.
Und dann müssen wir
die Transferzeit mit berücksichtigen
Okay, und dann können wir die schnellste Montagefahrt natürlich aus diesen optimalen Teillösungen zusammensetzen.
Wir sagen einfach, die schnellste Montagefahrt von S1,
J-Mews 1 zu S1J, können wir entsprechend so berechnen.
Und das Ganze können wir rekusiv einfach machen.
Und rechnen dann uns durch diesen Grafen durch,
bis wir dann für den letzten Schritt das Optimum berechnet haben.
Okay, so sähe das aus. Und wir sehen offensichtlich
die Eigenschaft der optimalen Teilstruktur. Das heißt,
die optimale Lösung des Problems beinhaltet optimale Lösung von Teilproblemen.
Die optimale Lösung von hier nach hier zu gelangen,
Inhalte, die optimale Lösung, um von dem Startpunkt zu
dieser Station 4 zu gelangen oder zu dieser Station 4.
Das ist darin enthalten. Okay, damit können wir
dynamische Programmierungen anwenden und jetzt müssen wir es nur machen.
Und was wir tun werden ist, wir machen erst die rekusive Variante.
Wir sagen mal, okay, wie können wir das Problem rekusiv definieren?
Und dann überlegen wir uns, wie können wir das überführen
in dem Bottom-Up-Ansatz, wo wir einfach Zwischenergebnisse speichern.
Gut, wir haben eigentlich gerade die Lösung schon beschrieben.
Wir müssen sie nur noch aufschreiben. Wir müssen sie
ein bisschen formalisieren. Das machen wir wie folgt.
Wir sagen jetzt F-Stern ist also die optimale Zeit und F-I-I-J
werden wir jetzt immer berechnen
Und I ist hier das Band, also Montage,
Band 1 und Montage, Band 2. Und J ist die Station.
Und zwar gucken wir uns immer die Zeit an,
bis nach dem Montageschritt. Also wenn hier die Station J3 ist,
bedeutet das, wie lange brauchen wir,
bis der Schritt 3 abgeschlossen ist auf diesem entsprechenden Band.
Okay, dann können wir sagen,
für den Schritt 1 ist das ganz einfach.
Wir haben also die Zeit E1 plus A11. Können wir mal
auf das Bild. Also wie lange brauchen wir,
um hinter dieser Station 11 zu kommen?
Also E1 plus die Montagezeit hier
Ja. Und für einen beliebige Station, irgendwo in diesem Netzwerk können wir das Ganze rekursiv beschreiben.
Also wenn wir auf Band 1 sind und an der Stelle J,
dann wissen wir, wenn J gleich 1 ist,
es ist E1 plus 1, 1, 1. Klar,
das hatten wir gerade. Ansonsten sagen wir,
entweder hier in dem Teil kommen wir von Band 1 von dem vorherigen Schritt,
das ist F1 von J minus 1. Bis dahin, plus die Montageschritt,
um für das J für den J einen Schritt plus A1 J. Oder wir haben das Band gewechselt,
dann kommen wir vom zweiten Band, also F2 J minus 1,
plus die Transfer 2, von zwei nach eins,
für dieses Schritt J minus eins,
plus die Montagezeit an dem Schritt.
Das sind die zwei Möglichkeiten, die wir haben.
Und das können wir für das Band 2 natürlich analog definieren.
Also hier ist für Band 1 definiert, wir waren es 2 definiert.
Okay, so, hier nochmal als Bild. Hier steht nur nochmal die Formel,
die wir gerade schon gesehen haben, zur Erinnerung,
willst du nochmal gucken können, wie das zustande kommt?
Wir nehmen mal ein Beispiel, wir nehmen mal hier diesen Punkt,
der hier durch diesen orangenen Strich markiert ist,
der ist extra so ein bisschen nach rechts versetzt,
um zu zeigen, markiert jetzt die Stelle,
wo die Station 1,4 passiert wurde,
wo diese Montage abgeschlossen wurde.
So, und was gilt jetzt? Wir können sagen,
okay, also um die Zeit hier zu berechnen,
nehmen wir einfach die Zeit des Schrittes davor für diese beiden Bände.
Die optimale Zeit, die wir haben,
um zur Station 3 zu gelangen und entweder führen wir einfach
nur die Montage durch oder wir führen den Bandwechsel durch und dann die Montage.
Hier konkret, wenn man das hier durchspielt von vorne nach hinten,
dann sieht man, hier kommt 24 raus,
wie kommt die 24 zustande,
also vier Einheiten,
hier sind die Montage
Zeit hier an der Stelle, vier Einheiten. Das heißt,
wir haben, wir gehen zur Station S23 zum Beispiel,
haben wir 22 Einheiten, plus zwei Einheiten, da müsste ich sich hin,
jetzt muss ich mir erstmal zwei, vier, sechs.
Achso, das ist ja nur das Minimum, klar.
Also wir haben zwei Möglichkeiten, wir sind,
wir fangen mal hier oben an, wir sind an der Studition S-S-13,
da haben wir 20 Einheiten, wir machen die Montage,
haben wir 24 Einheiten, sind an der Station S2, 3.
Da hätten wir 22 Einheiten, zwei Einheiten Transfer,
macht 24 plus die Montage, macht 28.
Und hier in der Tabelle steht natürlich noch das Minimum,
also 24 war das Minimum von diesen beiden Optionen, die wir hatten.
Und dann können wir das weitermachen, wenn wir uns alles berechnen.
Okay, so können wir das machen und am Ende,
am letzten Schritt müssen wir einfach nur das Optimum wählen zwischen
diesen beiden Stationen plus die Zeit, um hier die Ausfallzeit.
Gut, damit können wir das Problem lösen.
Wir definieren uns eine rekusive Funktion, die wir jetzt
einmal aufrufen müssen
Hier sehen Sie eine ganze Reihe Parameter,
das entspricht den Bildern, die wir davor hatten,
also A, sind die Montagezeiten, Thesen, die Transferzeiten E,
sind die Einführungszeiten am Anfang und X, die Ausführungszeiten,
also das sind die E-Zeiten, das sind die X-Zeiten hier.
Und M ist die Anzahl der Montageschritte, die wir haben. Okay,
und dann sagen wir, okay, wir wissen,
das ist das Minimum der rekrusiven Berechnung des kürzesten Weges,
des kürzesten,
der kürzesten Montagezeit,
zu Überband 1 plus X1 oder entweder überband 2 plus
Also das ist das Band, an dem wir rauskommen hier hinten.
So, und dann können wir das rekrusiv, die rekrusive Funktion eingeben.
Wir sind jetzt an dem Schritt J, den wir
jetzt berechnen wollen. Und wenn J gleich eins ist,
dann sind wir ganz am Anfang, also hier,
ja, da wissen wir es ganz einfach.
Das ist einfach nur E-1 plus A-1-1,
beziehungsweise, wir können ja jetzt auch Band 2 haben,
diese Funktion gilt ja für beide Wänder.
E ist dann entweder eins oder zwei. Und dann wissen wir,
dass es E-2 plus A-2-1 ist. Okay. So,
wenn wir nicht an dem ersten Knoten sind,
sondern irgendwo in der Mitte,
zum Beispiel wie bei dem Knoten,
wo wir es gerade gesehen haben,
müssen wir minimieren über diese zwei möglichen Fälle
Und die stehen dann hier als FI,
also weil Band 1 ist das I gleich 1 und 3 minus East,
dann 2. Also hier steht 3 minus I,
ganz einfach, wenn E dann 1 ist,
ist das 2 und wenn E2 ist das 1.
Das ist einfach nur das andere Band. Okay, also F1 ist,
wir bleiben auf dem Band I und dann sagen wir,
okay, wir nehmen einfach den kürzesten Pfad,
den wir hatten, zu dem Band Ji minus 1.
Zu der Position Ji minus 1 auf dem gleichen Band I.
Und andernfalls machen wir den Bandwechsel von dem 3 minus I-Band,
also wenn I jetzt eins war, vom Band 2 auf Band 1.
Und dann müssen wir,
wir brechen den kürzesten Weg um zu dieser Station zu gelangen,
also drei minus East, die andere J minus eins,
die andere Station, plus Transferzeit, plus Montagezeit,
um da hinzukommen.
Und da müssen wir das Minimum nehmen von den beiden.
Ja. Okay, Laufzeit, Rekursion, kommen wir durch eine Rekurrenzgleichung beschreiben.
Also die Laufzeit für Recursive Fast is Way von N,
also Endstation ist C, für N gleich eins und zweimal
die Laufzeit von der Funktion für die Eingabegröße N minus eins,
weil wir haben hier immer einen J und dann starten wir
es hier mit J minus eins,
zweimal plus 10.
Was kommt da raus
Ja, zwei auch N auf alle Fälle.
Zwei Wochen N. Also exponentielle Laufzeit. Sieht hier gar nicht
so schlimm aus, aber wir verdoppeln immer.
Zwei rekrusive Aufrufungen. Das N wird nur um eins kleiner.
Sehr ungünstig. So, also was passiert? Hier ist
die Rekurrenz aufgelöst, aber das sollten sie eigentlich mittlerweile auch selber können.
Hier ist es einfach nur ein paar Mal eingesetzt.
Ja, wenn sie das N minus eins mal einsetzen,
haben sie zweimal, zweimal, zweimal, zweimal und so weiter.
Und hier ist dann die Zeit für den rekursiven Aufruf mit
der Konstanz
Und dann haben sie hier plus C, plus Epos
D. N minus einmal. Und wenn sie das aufschreiben,
steht die 2 auch endminus 1 mal C plus.
Hier ist die Summe von I gleich 0 bis N minus 1,
2 hoch IC. Und wenn sie das jetzt zusammenfassen,
kommen sie genau auf 2 hoch N. Und hier nochmal,
damit sie das, wird wird für die,
die das so ein bisschen bildlicher haben wollen.
Also hier haben sie jeder, jede Ellipse steht hier für einen rekursiven Aufruf.
Und die Zahl da drin steht quasi für die Zeit,
nein, nicht für die Zeit, sondern für die Größe,
die sie brauchen, um das Ganze des Parameters.
Also hier rufen sie es auf mit
Dann rufen sie es auf mit N minus eins, zweimal,
dann rufen sie es viermal auf mit N minus zwei,
dann rufen sie es achtmal auf mit N minus drei und so weiter.
Und dann sehen sie, dass sie hier unten zwei hoch
N minus eins viele Aufrufe haben, alle mit eins.
Okay, also zwei hoch N-Zeit und zwei hoch N-Zeit zur
Berechnung dieser Tabelle, weil mehr passiert ja nicht.
Also wir berechnen zwei N, viele Zahlenwerte.
Das ist das N hier, wie viele Schritte wir haben.
Zwei Bände, zwei N, viele Zahlenwerte berechnen.
Wir brauchen zwei Hochendzeit, um das zu tun.
Es ist offensichtlich nicht der Weg, wie man das machen sollte.
Warum dauert das
so lange
Das dauert deswegen so lange, weil wir hier immer wieder
die gleichen Teilprobleme lösen. Wir fragen immer wieder nach dem
gleichen Teilstück und fragen, was kommt denn da raus?
Und dann berechnen wir es rekursiv eben nochmal. Wenn wir
uns das Ergebnis merken würden, dann würden wir den rekursiven Aufruf einsparen.
Und dann müssen wir das nichts mehr so häufig machen.
Okay, so, also hier ist die Art und Weise,
wie man das machen soll.
Wir ändern jetzt also diesen rekursiven Top-Down-Ansatz.
Also wir sagen N, wie groß ist das?
Zerlegen, rekursiv berechnen, Ergebnis zusammenbauen in einem Bottom-Up-Abensatz.
Wir sagen, wir berechnen mal die Ergebnisse für kleine Teilstücke
und bauen dann rekursiv,
bauen dann iterativ quasi das Ergebnis zusammen
Dazu müssen wir uns irgendwie die Ergebnisse merken. Wir müssen
irgendwie was speichern und das machen wir jetzt in so einem entsprechenden Array.
Das ist jetzt dieses Array F. Das sieht jetzt hier,
also für Pseudo-Code ist es vielleicht jetzt noch ein bisschen ungewöhnlich,
dass ich Indizes verwende, wenn sie das jetzt hier so sehen.
Ich habe das bewusst so gelassen,
auch wenn der Pseudo-Code natürlich eigentlich jetzt eckige Klammern müssen,
weil ehrlich gesagt, hier kommen so viele Indizes vor.
Das ist ein bisschen leichter zu lesen,
wenn das eine hier, das ist der Index hier selber,
ist immer das Band und das andere ist die Station.
Dann kommt man nicht so durcheinander. Sonst,
wie wir es anders haben wollen,
können sie es natürlich auch ganz schnell anders aufschreiben,
schreiben es alles mit eckigen Klammern
Okay, also wir berechnen zwei Arrays, also F1,
F2, wenn sie so wollen.
Und hier der Index, in dem Array ist die Bannstation.
Und das, was wir eigentlich machen, ist,
wir berechnen genau diese beiden Arrays hier.
Diese Werte berechnen wir. Und gerade habe ich ihnen gezeigt,
wie wir es machen. Also von dem Punkt aus
habe ich ihnen gesagt, wie wir es berechnen können.
Wir gehen an eine Stufe zurück, gucken uns diese Zahlen an.
Wir hatten diese Formel aus und kriegen den Wert für das.
Das heißt, wir müssen quasi in dem Array von der
Position J gleich eins schrittweise nach vorne gehen bis zu sechs.
Und nicht bei sechs anfangen und rekursiv fragen,
was kommt denn da raus und dann die Berechnung für fünf anstoßen.
Sondern von den kleinen Zahlen zu den großen Botmark
Okay, so, das machen wir. Wir sagen,
was kommt denn für die erste Station raus?
Das wissen wir. Band 1 ist E-1 plus A-1-
1, Band 2 ist eh 2 plus A-2-1.
Dann kommt die Vorschleife. Jetzt gehen wir also durch die
verschiedenen Stationen und fragen immer, was ist die kürzeste Montagezeit,
um zu dieser Station zu gelangen?
Ja, das machen wir hier. Und das können wir einfach ausrechnen,
weil wir wissen ja, was die kürzeste Zeit ist,
um zu der Station davor zu gelangen.
Das haben wir ja in der vorherigen Iteration schon berechnet. So,
das heißt, diese Schleife geht jetzt von 2 bis N und wir berechnen jeweils,
dass F, also für das Bandeis und Band 2,
jeweils die Zeit, die wir benötigen,
hier Für den zweiten Montageschritt. Und das ist entweder,
also wir gucken uns das jetzt für Band 1 an,
entweder wir nehmen die Zeit, die wir benötigen,
um von Band 1 zur Position J minus 1 zu kommen und addieren die Montagezeit.
Oder wir nehmen die Zeit, die wir benötigen,
um auf Band 2 bis zur Position J minus 1 zu kommen,
nehmen die Transferzeit und die Montagezeit.
Sind die beiden Möglichkeiten, die wir haben.
Und das Gleiche für F2, also fürs andere Band,
das ist natürlich das Gleiche.
Es ist symmetrisch einfach, ja. Und dann können wir
das Gesamtergebnis berechnen. Es ist einfach nur das Minimum von
F1N plus X1, dieser Ausfahrtzeit und F2N
Ja, Laufzeit ist wohl offensichtlich linear Zeit. Also nur durch das Umdrehen,
durch das Vermeiden der Rekursion, das Ersetzen der Rekursion,
durch die Iteration und das zusätzliche Speichern dieser Erfährte,
das muss man natürlich auch noch tun,
haben wir gerade die Laufzeit von einer exponentiellen Laufzeit in einer Linearzeit verwandelt.
Ja, was faktisch bedeutet, ja,
das exponentielle Verfahren wird wohl spätestens bei ein paar hundert scheitern.
Ja, für Ende. Das können sie bis Milliarden
machen auf dem Rechner, ohne Probleme. Gut,
ich möchte ihm mal gerade spaßeshalber ein anderes Problem nennen,
was Was so ähnlich ist wie das hier.
Und das ist ein Bio-Informatik-Problem, wenn sie,
genomische Sequenzen haben, sie beim Buchstaben folgen.
Da stehen jetzt irgendwie, steht ihre Erbinformation.
Und sie fangen an, die zu vergleichen, diese Erbinformation.
Dann müssen sie sagen, was ist das beste Alignment?
Also was gehört zu was? Und wie können sie
das zuordnen? Das ist genau so ein Zuordnungsproblem,
was fast analog zu diesen Montagebändern passt,
weil sie diese zwei Zeichenketten haben,
die sie vergleichen wollen
Wenn sie das machen, auf den Human genommen, mit 4 Milliarden Buchstaben,
dann sollten sie auf keinen Fall das rekusive Verfahren verwenden.
Das Iterative würde für sowas durchaus funktionieren. Okay,
übrigens alle Arten von String-Vergleich,
wenn sie sich nicht so sehr mit Biologie auseinandersetzen wollen,
alle Arten von Zeichenkettenvergleich, wenn sie Textmining machen,
wenn sie große Textpassagen scannen,
Parsen vergleichen,
basieren quasi auf so einem dynamischen Koranierkonzept oder können damit gelöst
werden.
So, ja, Problem gelöst. Also ehrlich gesagt natürlich noch nicht ganz,
ja, weil das Ergebnis dieses Algorithmus ist die Zeit.
Da kommt jetzt raus, das Ganze dauert, keine Ahnung, 46 Stunden.
Aber sie wissen ja gar nicht, wie. Ja,
sie haben jetzt die Zeit berechnet. Hier kommt raus,
38, ja, in dem Beispiel. Aber klar,
es sagt ihnen der Algorithmus super, sie brauchen 38 Stunden,
aber das wollte ich ja gar nicht wissen.
Das wollten sie auch wissen, aber sie wollen natürlich wissen,
wo geht denn dieser Fahrt hier lang?
Ja, das Ergebnis haben sie jetzt nicht. Das ist
ein ganz typisches Problem bei der dynamischen Programmierung dass man häufig
einen Extraaufwand betreiben muss, um überhaupt dieses optimale Resultat zu extrahieren.
Das ist hier verloren gegangen auf dem Weg. Und wir
müssen irgendwie dafür sorgen, dass wir das rekonstruieren können.
Das ist für uns auch nicht neu. Das haben wir
schon ganz häufig gesehen. Also hier, kürzeste Pfade.
Also wir haben das immer implizit mitgemacht, weil im kürzesten
Faden haben wir immer den Vorgänger gespeichert mit diesem Pi.
Haben wir gesagt, Pi von E ist dann ein Vorgänger,
entlang des kürzesten Pfades. Das hat genau den Zweck gehabt,
damit wir nachher nicht nur sagen, der Knoten ist 17 Kilo,
Sondern dass wir auch sagen können, und da müssen wir lang gehen.
Das müssen wir hier auch machen. Und das muss man
typischerweise bei dynamischer Programmierung machen. Man muss Zusatzinformationen speichern,
um die optimale Lösung auch rekonstruieren zu können.
So, wie machen wir das? Das machen wir wie folgt
. Hier sehen Sie den gleichen Algorithmus nochmal wie gerade.
Der sieht irgendwie viel länger und viel komplizierter aus.
Das täuscht aber. Das Einzige, was ich gemacht habe,
ist, hier gibt es dreimal so ein Minimum,
da, da und da.
Und ich habe aus diesem Minimum
ein IFLs gemacht
Also es ist ja eigentlich eine Bedingung, wenn das kleiner ist
als das, dann nimm das, sonst nimm das.
Das habe ich jetzt hier hingeschrieben. Also der gleiche Algorithmus wie eben,
und hier ist das If-Ls, ist das erste Minimum,
das If-Ls, das zweite Minimum.
Und hier ist nochmal ein If-Ls ist das dritte Minimum.
Also es ist genau der gleiche Algorithmus. Eins zu eins
gleich. Das habe ich jetzt so hingeschrieben,
weil wir müssen den jetzt ergänzen und wir müssen irgendwie Informationen speichern,
um nachher das optimale Ergebnis zu rekonstruieren.
Und offensichtlich ist genau diese Entscheidung, welches Montageband wir wählen,
natürlich die Informationen, die wir uns merken müssen.
Und genau die Entscheidung fällen wir bei dieser Minimum
Deswegen habe ich das aufgelöst in so ein E-Funden-Ls.
Okay, müssen wir jetzt nicht nochmal angucken, haben wir gerade schon gemacht.
So, was machen wir? Wir berechnen einen zusätzlichen Wert,
ähnlich zu diesen P-Werten bei den kürzesten Faden und sagen,
wir müssen irgendwie wissen, welche Entscheidung wir gefällt haben,
um zu einem speziellen Punkt zu kommen.
Also wenn wir zum Beispiel diese Station 4 nehmen,
wir sehen hier so grau unterlegt,
das ist der kürzeste Pfad für dieses Netzwerk, für dieses zwei Montagebänder.
Wenn wir jetzt an dieser Station 4 sind,
dann müssen wir wissen, ob zu der kürzesten Zeit,
die wir hier berechnet haben für die Station 4,
wir die erhalten haben,
weil wir das Band gewechselt haben oder weil wir auf dem gleichen Band geblieben sind.
Diese Information brauchen wir noch. Wenn wir die haben,
dann könnten wir von hinten aus diesem Pfad rückwärts rekonstruieren.
Ja, die Information müssen wir speichern. Das machen wir in einem zusätzlichen Array,
das heißt L, ja, und dieses Array L,
das gibt es auch für Montageband 1 und 2
und es gibt es für jede Montageschritt.
Also quasi für jeden, Lisa, Station.
Ja, merken wir uns diesen Wert.
Und hier ist es wieder gezeigt für diesen einen Knoten hier,
also für den Wert merken wir uns einfach,
von welchem Band kommen wir eigentlich, wenn wir hier den optimalen Weg haben?
Kommen wir vom Band 1 oder kommen wir vom Band 2?
Deswegen stehen hier in dem Array auch nur Eisen und Zwein
Also ich sage einfach nur, von welchem Band
wir gekommen sind. Und hier steht jetzt drin,
umzubanden, um zu Montageschritt 4 auf Band 1,
den zu gelangen mit dem kürzesten Pfad,
müssen wir vorher auf Band 1 gewesen sein.
Also wir müssen dann wechseln. Das steht da drin. So,
okay, das ist die Zusatzinformation, die wir brauchen.
Das entspricht ziemlich genau auch diesem P-Werten bei den kürzesten Wegen.
Und die müssen wir quasi jetzt mitberechnen in dem Algorithmus.
Das ist allerdings nicht so besonders schwierig, was wir einfach machen,
ist, wenn wir die Entscheidung fällen,
ob wir den kürzesten Pfad hier lang oder hier lang folgen,
dann können wir ganz einfach uns merken, Band 1 oder 2 war.
Das machen wir in diesem Array. Und so sieht es
dann vollständig aus. Also das, was jetzt rot ist,
ist das, was eingefügt wurde. Und wir sehen einfach,
wir merken uns für jeden Schritt, über welches Band wir gekommen sind.
Also hier, wenn die Montagezeit, wir sind auf Band 1,
an der Station J. Und wenn die Zeit von dem gleichen Band 1 zu diesem Schritt kürzer war,
als die mit einem Bandwechsel von Band 2,
dann nehmen wir natürlich die Zeit und wir merken uns,
dass wir vom Band 1 gekommen sind.
Wenn wir den Excel des Bandes akzeptiert haben, dann haben wir das hier gemacht.
Dann merken wir uns, dass wir von Band 2 kommen.
Hier kommt die Berechnung für Band 2. Das ist natürlich umgekehrt.
Das ist 2- und 1 vertauscht, weil hier sozusagen ist der Fall,
dass wir auf dem gleichen Band bleiben.
Das ist jetzt aber Band 2, also L2 von JS2.
Hier haben wir das Band gewechselt, L2 von JS1. So,
und für das Ergebnis müssen wir uns auch nochmal merken,
über welchem Pfad wir eigentlich, also was die letzte Station war,
ob die auf Band 1 oder auf Band 2 war.
Okay, wenn wir diese Informationen haben, können wir die optimale Lösung rekonstruieren. Das machen wir,
indem wir jetzt einfach rückwärts von dem End Punkt durch das
Netzwerk laufen und ihr wollt die Entscheidung anschauen und dann zu diesem entsprechenden Punkt gehen,
würden dann sagen, okay, hier wissen wir,
wir sind von Band 1 gekommen, das stimmt,
hier so hoch.
Dann gucken wir hier, hier steht, um hier die
optimale Lösung zu haben, mussten wir von Band 2 kommen.
Das stimmt, hier haben wir das Band gewechselt und hier
die optimale Lösung zu kommen, mussten wir vom Band 2 kommen.
Hier, das stimmt hier. Da haben wir das Band gewechselt,
zu 1, hier hoch, hier haben wir wieder gewechselt,
zu 2, hier runter, hier haben wir,
waren wir dann einen Punkt 1,
jetzt bin ich gleich durcheinander gekommen und dann,
nee,
hier wieder hoch und dann sind wir fertig
Okay, gut. Jetzt brauchen wir nur noch ein Verfahren,
was uns die Lösung ausgibt aus dieser Information und daraus einen Pfad macht.
Das ist einfach nur so eine Funktion,
die einfach diese Information abklappert, rückwärts und das Ergebnis hinschreibt.
Das macht die folgende Funktion hier, die heißt Print Stations,
also die Ausgabe der Stationen, die verwendet werden sollen.
Und hier fangen wir an mit El Stern.
Also L ist das gesamte Ergebnis, diese Informationen,
die wir gesammelt haben für die Rekonstruktion des Pfades.
Das ist am Anfang Jahr 1 gewesen.
Dann schreiben wir das Ergebnis raus,
Band I wäre jetzt
Hm. Ein Zippfehler. Nee, Band I,
das Band I ist das Band, über das wir jetzt gekommen sind.
Das stand hier in den Elf Sternen drin.
Und dann kommt die Station, ist die Station Nummer N.
Also die Ausgabe ist hier rückwärts. Und dann laufen wir
von J rückwärts von N runter bis 2. Und sagen dann,
das I ist das LI von J. Wir lesen das aus
der Tabelle hier aus und machen dann die Ausgabe.
Wen das jetzt stört, dass das rückwärts ausgegeben wird.
Die erste Ausgabe ist ja,
was muss ich am Band an Station 6 machen,
an einen 5, an einen 04,
dann an drei,
an zwei Jahren,
dann bin ich
irgendwie fertig
Sich. Der kann das natürlich ganz schnell ändern,
aber sie können nicht einfach diese Schleife umdrehen.
Sie können nicht einfach sagen, okay,
dann laufe ich jetzt hier eben von 1 bis N.
Ja, es geht natürlich nicht,
weil sie folgen hier ja einem Pfad in der Matrix.
Wenn sie das umdrehen wollen, müssen sie diese Prince Stations Funktion rekursiv machen,
also endrekursiv und die Ausgabe dann sozusagen ans Ende schreiben.
Dann geht das. Nicht, dass sie jetzt denken,
das dauert dann ja ganz lange,
weil es rekursiv ist und nicht alles dauert lange,
weil es rekursiv ist.
Das geht natürlich auch in der NR-Zeit,
weil sie auch immer nur quasi erst den Weg hier berechnen
und dann immer einen Schritt machen und dann wieder zurücklaufen
Okay, gut, damit können Sie das Ergebnis ausgeben. Gut,
das ist ein Beispiel für dynamische Programmierung, sehr schönes,
einfaches Beispiel, an dem man sehen kann, wie diese vier Schritte ablaufen.
Wir können eigentlich immer davon ausgehen, dass man diese vier Schritte,
die wir am Anfang gesehen haben, also erst mal überlegen,
wie sieht die optimale Teilstruktur aus und existiert die überhaupt,
dann das Rekusive Ergebnis berechnen,
dann die rekusive Lösung umwandeln,
in einem Bottom-Up-Ansatz und dann die richtige Lösung rekonstruieren,
dass diese vier Schritte eigentlich immer auftreten bei der dynamischen Programmierung,
die muss man
immer einsetzen
So, Zeit für ein richtig knackiges Problem,
was wir mit dynamischer Programmierung lösen wollen.
Also nicht so ein Spielproblem, sondern ein richtig schwieriges,
was wir auch nicht ganz einfach mit kürzeste Wege lösen könnten.
Das Problem kommt aus der, ja, aus der Nomerik,
aus der Matheuhr, aus der Mathematik.
Und es muss sich jetzt auch gar nicht so abschrecken.
Wir werden ganz schnell, ich werde hier nur das Problem
beschreiben und warum das da auftritt.
Und dann werden wir ganz schnell einfach, es hat ein
Optimierungsproblem betrachten. Was man macht, ist, man will
Matrizen multiplizieren
Und zwar ganz viele, möchte mal miteinander multiplizieren.
Und so eine Matritzen-Multiplikation. Ich hoffe, Sie wissen noch
so grob, wie das geht. Also wir haben zwei Matritzen,
die haben hier N, dieser hat N-Zeilen und M-Spalten.
Diese hat eher Zeilen und Ess-Spalten. Und wenn Sie das Produkt bilden,
dann müssen wir immer Zeile, mal Spalte multiplizieren.
Und dann kommt hier entsprechend eine N-Ma-S-Matrix raus.
Und das ist Produkt A mal B. Also immer Teile,
mal Spalte. Hier steht die mathematische Definition des Produktes,
also A als Matrix,
das sind die AIJs und das I läuft von 1 bis N,
das J von 1 bis M,
W,
Der läuft von 1 bis R und J läuft von 1 bis S.
Und hier ist das Produkt, dann hoffen wir von Kargleich
1 bis M, AI-K mal BKJ müssen wir ausrechnen.
Okay, wie geht das? Ja,
das müssen wir nur ausrechnen hier für alle Matrix-Elemente.
Das ist also mein Pseudo-Code hier für eine Matrix-Multiplikation.
Wenn A von S ungleich B von Z ist,
also A von Punkt S, also S ist die,
muss ich noch gerade sagen,
S ist die Anzahl der Spalten und Z ist die Anzahl der Zeilen.
Also wenn die Anzahl der Spalten ungleich der Anzahl der Zeilen sind,
der zweiten Matrix, dann sind die inkompatibel,
dann können wir
die nicht multiplizieren
Ja, das ist also hier nur so eine Fehlermeldung,
die dir dann ausgegeben wird. Ansonsten betrachten wir alle Kombinationen IJ,
dieser resultierende Matrix, vor ihr ich eins bis a.z,
vor J gleich eins bis b.s.
Und für jedes dieser Elemente werten wir diese Formel auf.
Wir haben hier einen CEJ, das ist das Ergebnis,
es wird auch mit null initialisiert, die Vorschleife läuft von eins bis a.s,
also hier entsprechend die Anzahl der Spalten durch.
Das ist also diese Summe hier und wir rechnen immer CEJ
plus gleich und dann AIK mal BKJ, AIK mal BKJ.
Adieren wir alles auf in dieser Vorschleife und dann
haben wir das Ergebnis und C ist dieses Ergebnis Matrix
Okay, ja, wie lange dauert das? Ja,
also wir können die Zeit angeben, abhängig von der Anzahl der Zeilen und Spalten. Also es ist irgendwie A von Z,
also Anzahl der Zeilen in Matrix A,
mal Anzahl der Spalten in Matrix A,
mal Anzahl der Spalten in Matrix B. Oder, weil sie wissen,
die Anzahl der Spalten in Matrix A ist gleich der Anzahl der Zeilen in Matrix B. Können Sie auch sagen,
Anzahl Zeilen, Matrix A, mal Anzahl Zeilen,
Matrix B, waren es als Spalten, Matrix B.,
also einfach die drei Vorschleifen hier betrachtet,
ist die Laufzeit
Wer glaubt, dass das optimal ist, zur Berechnung dieses Werts?
Wer glaubt das nicht optimal? Okay, es gibt einige,
die kennen sich aus. Es ist tatsächlich nicht optimal.
Also man kann die Matrix-Multiplikation schneller berechnen als mit diesem Verfahren hier.
Das geht. Es war lange, hat da ehrlich gesagt,
also ehrlich gesagt hat da gar keiner drüber nachgedacht,
weil diesen Algorithmus zu versuchen zu optimieren,
zur Berechnung dieses Wertes ist schon echt verwegen.
Also zu sagen,
das kann man ja noch besser und schneller machen.
Es geht schneller
Also das ist ja, wenn die Matrizen alle mal Dimension N hätten,
wäre das so wie N hoch 3, die Laufzeit.
Der erste, der es geschafft hat, das schneller zu machen,
war Straßen, der hat das hingekriegt mit N hoch 2,81,
so um den 3 roundabout.
Und der hat dann quasi den Weg gezeigt, wie man gehen muss.
Und danach haben es noch andere irgendwie verbessert.
Momentan kann man das machen mit N hoch 2,3 und noch
ein paar Nachkommastellen. Ist für uns weiter nicht wichtig,
ist nur ein weiterer Pointer in die Algorithmus,
wenn Sie wissen wollen, wie das schneller geht als N
hoch 3. Können Sie
das da sehen
Aber wir wollen ja eigentlich Matrizen multiplizieren in einer Kette.
Also wir haben jetzt die Anordnung, viele Matrizen hintereinander.
Und für die Matrizen-Multiplikation gilt das assoziativ Gesetz.
Das Kommuntativgesetz gilt nicht. Amal B ist nicht notwendig,
aber das Gleiche wie B mal A. Aber das Assoziativgesetz gilt,
also A mal B mal C,
können sie entweder B mal C rechnen und dann mit A
multiplizieren oder A mal B berechnen und dann mit 10 multiplizieren.
Das ist egal, da kommt das Gleiche raus.
Die Frage ist, dauert das auch gleich lang?
Ist das egal, ob ich das so mache oder so mache?
Ein Beispiel
So. Ein Beispiel ist,
drei Madritzen, das erste ist eine zehnmal 100 Matrix,
dann haben sie eine 100 mal fünf Matrix,
dann haben sie eine fünfmal 50 Matrix.
Jetzt können sie, wollen sie eins mal zweimaler drei ausrechnen.
Sie können eins maler zwei ausrechnen und dann mit A3 multiplizieren
oder sie können A2 mit A3 multiplizieren und dann nachher eins mit dem Ergebnis.
Das ist die beiden Möglichkeiten, die sie haben.
Wenn sie A1 mit A2 multiplizieren, resultiert eine zehnmal fünf Matrix.
Wenn sie A2 mit A3 multiplizieren, entsteht eine 100 mal 50 Matrix.
Und das ist natürlich das Ergebnis, was sie dann im
nächsten Schritt brauchen. Wenn sie dann A1 mal A2,
das Ergebnis davon mit A3 multiplizieren, brauchen sie 7500 Multiplikationen.
Wenn Sie aber AS A2 mit A3 multiplizieren und dann
mit A1 multiplizieren, brauchen Sie 75.000 Multiplikationen.
Also schon bei diesem ganz einfachen Beispiel haben wir einen Faktor 10
an Laufzeit, weil die Laufzeit, die wir brauchen,
ist proportional zur Anzahl der Multiplikationen.
Also das sind die, also die Multiplikationen hier ganz unten
in dem Algorithmus drin. Wenn Sie sich jetzt vorstellen,
Sie haben so eine Kette von 10 oder 20 Matrizen,
die Sie multiplizieren wollen mit unterschiedlichen Dimensionen,
dann kann das ganz schnell ganz übel enden.
Das heißt, es lohnt sich, darüber nachzudenken,
in welcher Reihenfolge sollten wir diese Matrizen multiplizieren?
Das ist also das Problem der Matrix-Kettenmultiplikation. Wir haben also
eine Sequenz von Matrizen, A1, A2 bis AN.
So, dann interessiert uns die Reihenfolge der Matrix-Multiplikation.
Und zwar die vollständige Klammerung dieses Ausdrucks. In welcher Reihenfolge
sollen wir das machen? Und zwar wollen wir die Anzahl
der Skalaren-Multiplikationen minimieren. Also der Multiplikation hier zwischen Matrix-Elementen,
also die Gesamtlaufzeit ist das dann
Okay, wir werden das Produkt selber nicht berechnen,
das ist wieder so eine Frage, wir interessieren uns jetzt
nur für den Weg, um das zu tun.
Wenn Sie den Weg haben, können Sie nachher die Matratze Multiplikation dann ausführen.
Okay, so, das ist die Fragestellung. Und die
erste Frage ist natürlich, ist das überhaupt ein schwieriges Problem?
Wie viele Möglichkeiten gibt es denn überhaupt, dieses Produkt von N-Matrizen auszurechnen?
Also wie viele Möglichkeiten gibt es hier, Klammern zu setzen?
Also es ist nicht so ganz so trivial wie bei den Montagebändern,
weil jetzt kann man,
man kann ja nicht irgendwie die Klammern setzen,
man muss ja auch noch
ein sinnvoller Klammerausdruck rauskommen
Deswegen muss man sich das überlegen. Also wie groß ist denn
dieser Lösungsraum? Man kann dann sagen, ja okay,
wenn ich eine Matrix habe, ist das ganz einfach,
gibt es nur eine Möglichkeit, da muss ich gar nichts multiplizieren.
Bei zweien gibt es auch noch eine Möglichkeit,
ich rechne A mal B. Wenn ich drei Matrizen habe,
das haben wir gerade schon gesehen,
da gibt es zwei Möglichkeiten, A mal B,
dann mal C oder B mal C und dann mal A mit dem A multiplizieren.
Für vier ist das erste Mal, wo man nachdenken muss,
da kann man die mal aufschreiben, habe ich hier gemacht für sie.
Das sind die vier, die fünf Möglichkeiten, wie sie
vier Matrizen klammern können, wenn sie die ausrechnen wollen.
Sie können erst CD multiplizieren, damit bämen wir mit A oder BMC,
dann mit D und dann mit A. Hier hatten wir zum Beispiel BMC,
dann mit A, dann mit D.
Also wir sehen, da gibt es verschiedene Kombinationsmöglichkeiten.
Hier gibt es fünf. Und die Frage ist,
was passiert, wenn das N jetzt immer größer wird?
Und naja, mal angenommen, wir haben jetzt ein großes N.
Dann können wir uns überlegen, okay,
zwischen welchen zwei Matrizen machen wir denn die letzte Multiplikation?
Und wenn das an der Position K ist, mal angenommen,
die letzte auszuführende Multiplikation ist zwischen K und K plus 1,
dann haben wir da das untereinander geteilt.
Dann können wir reklusiv definieren, wie groß das P ist. Dann sagen wir nämlich,
okay, also wenn es an der Position K ist,
dann haben wir P von Aminus-Möglichkeiten für die vordere Sequenz und P von N-K-Möglichkeiten für die hintere Sequenz.
Und dann haben wir ja natürlich auch noch verschiedene Möglichkeiten,
diesen Positionen K für die letzte Multiplikation zu wählen.
Das heißt, P von N ist entweder eins,
wenn das N noch gleich eins ist,
oder es ist die Summe von K gleich 1 bis N minus eins.
K sind dann die Positionen für die letzte Multiplikation.
P von K um den ersten Faktor auszurechnen und P
von N-K um den zweiten Faktor auszurechnen.
Und das müssen sie
alles aufsummieren
Diese Folge hier, die Sie hier sehen,
können Sie aufschreiben, die kennen die Mathematiker.
Es hat einen Namen, sind sogenannte Katalan-Zahlen.
Ich kannte die ehrlicherweise vorher auch nicht. Ich weiß nicht,
ob die Wodi in der Mathematik so überall auftreten.
Ich kenne sie ja aus diesem Kontext. Egal,
haben die Mathematiker glücklicherweise schon genau studiert und die haben rausgefunden,
dass das P von N mindestens so schnell wächst wie 4 hoch
N durch N hoch 2 Drittel. Und für uns
relevant ist eigentlich nur das 4 hoch N.
Also das ist ein exponentielles Wachstum, was wir hier sehen.
Und zwar noch wesentlich stärker als das Wachstum,
was wir eben bei den Montagebändern hatten.
Das ist im Wesentlichen 4 hoch
Wie das wächst. Okay, also sehr, sehr viele Möglichkeiten, die Klammern zu setzen.
Und wir brauchen eine gute Strategie, das Ganze zu machen. Ich hoffe,
es fällt Ihnen auch hier schon auf oder jetzt schon auf,
dass wir nicht ein einfaches,
so ein einfaches Konzept machen können, wie bei den Montagebändern,
dass wir einfach immer einen Schritt zurückgehen und überlegen,
wie wir es da gemacht haben,
weil wir hier irgendwie viele Möglichkeiten haben,
Teilprodukte entsprechend zu bilden.
Gut, wie wir das Problem lösen,
werden wir dann am Freitag sehen und wir lernen das mal
noch eine schöne Restwoche