So, guten Morgen. Wir schauen uns heute nochmal
den Abschluss der Analyse der Fibonacci Heeps an.
Und danach werde ich Ihnen noch eine weitere Baumstruktur vorstellen.
Das werden wir aber im Überblicksmodus machen,
zumal die Struktur auch technisch nicht so schwierig ist.
Und damit können wir dann heute dieses Kapitel auf alle Fälle abschließen.
Und ich werde ihn auch wahrscheinlich,
je nachdem wie es zeitlich hinkommt, schon mal zeigen,
wie es dann im nächsten Jahr weitergehen wird.
Ja, die Fibonacci Heeps haben wir in der letzten Vorlesung ganz ausführlich betrachtet.
Hier sieht man noch einmal die Struktur, die man anstrebt.
Das ist also ein Heep, den man realisiert,
dadurch, dass man eine Menge von Bäumen angibt.
Jeder Baum hier erfüllt die Heep-Eigenschaft, sodass das Minimum in
den Wurzeln steht
Und zyklisch verkettet, zyklisch doppelt verkettet,
genauso wie die Kindlisten,
Knoten können viele Kinder haben und jeder dieser Bäume ist im Prinzip ein vinumialer Baum,
wobei einige der Knoten hier eine Markierung haben, was bedeutet,
dass sie im Laufe des Prozesses von Einfügung und Löschen von Elementen bereits Kinder verloren haben.
Der Fibunachi Hieb hat halt das Ziel, die Quees-Operation,
so günstig wie möglich zu machen,
weil die eben häufig verwendet wird in den klassischen Datenstrukturen
und wir machen das auf Kosten der X-Track-Min-Operation
Ja, die Operation haben wir schon gesehen. Wir haben
auch die Analyse gesehen und im Rahmen der Analyse haben wir gesehen,
dass eine spezielle, ein spezieller Wert in der Datenstruktur ganz wichtig wird für die Laufzeit.
Das ist das D von N, das hier unten steht.
Das ist der maximale Grad eines Knotens in einem N-Elementigen Hieb. Also wenn wir wissen,
der Hieb hat irgendwie N-Knoten insgesamt in allen Bäumen gespeichert,
dann ist der maximale Grad eines Knotens,
der vorkommen kann, eben dieses D von N. Ja,
warum spielt das so eine Rolle?
Dazu muss man sich die Die Extract-Min-Funktion anschauen.
Die Extract-Min-Funktion ist die Funktion, die den großen Aufwand betreibt.
Und die Datenstruktur quasi wieder repariert, die Struktur wieder aufbaut.
Und das macht sie mit so einer Consolidate-Funktion. Also zunächst einmal
wird ein Element entnommen. Also das Minimalelement wird entnommen.
Die ganzen Kinder dieses Elements bilden quasi einen neuen,
etwas kleineren Fibunacci-Hieb. Und diese Wurzelisten,
die vereinigen wir in dieser Conquett-Operation hier in Zeile 4.
Und dann kommt dieser Consolidate-Schritt und dieser Consolidate-Schritt, jetzt,
der baut quasi aus Bäumen, aus kleineren Bäumen, größere Bäume wieder zusammen.
Die Anzahl der Wurzeln, die wir haben,
die steigt in diesem Schritt in der Zeile 4,
um die Anzahl der Kinder, die dieser Knoten Z,
den wir aus der Struktur entnehmen hat.
Und wenn wir dann die Laufzeitanalyse machen, dann sehen wir,
dass diese Anzahl der Kinder eben ganz entscheidend ist.
Und deswegen, und das entspricht ja dem D von N,
also die maximale Anzahl Kinder, die einen Knoten haben kann.
Und deswegen müssen wir uns die
eben genau anschauen
Ja, der Konsolidierungsschritt, den haben wir uns hier genau angeschaut,
was genau passiert ist,
dass wir ein Array verwenden mit D von N vielen Elementen.
Und dort speichern wir jeweils die Wurzeln der Bäume, die dann die Kinder haben.
Also in dem Eintrag D. Und wir werden,
also die Wurzelliste wird aufgelöst und das passiert hier in dieser Wildschleife, in jedem
dieser Array-Elemente kann maximal einen Baum mit Graz D an der Wurzel gespeichert werden,
wenn wir dann den zweiten Baum davon sehen,
dann führen wir unsere Link-Operationen aus, die aus zwei Bäumen einen Baum macht.
Und er hat dann die Ordnung D plus 1. Und dann
versuchen wir den in der nächsten Iteration dieser Wildschleife in der Area-Position D plus 1-1 zu fügen.
Okay, und das machen wir so lange,
bis wir eine Einfügeposition gefunden haben.
Und dann schreiben wir den Warm dahin. Am Ende führen wir
dann dieses Area wieder zurück in diese zyklische Liste von Knoten
und haben dann quasi wieder einen Fibunachi-Heat mit der zusätzlichen Eigenschaft,
dass jeder Wurzelknotengrad nur einmal vorkommt.
Ja, hier war das Beispiel dazu
Und in der Analyse haben wir dann gesehen,
dass einige der Schritte, der in der bezüglich der real auftretenden Laufzeit,
im Fond D von N abhängig, also von diesem maximalen Knotengrad,
zum Beispiel die Initialisierung des Arrays,
der Konsolidierungsschritt, betrachtet eben die Wurzeln.
Und das sind eben die Wurzeln, die wir vorher hatten,
plus eben diesen Knotengrad. Auch die innerhalb der Consolidate-Funktion müssen
wir genau diese Anzahl von Schritte auf alle Fälle durchführen.
Das heißt, das D von N spielt eine zentrale Rolle
in der realen Laufzeit, die auftritt,
wenn wir
diese Bäume wieder zusammenführen
Wir sehen dann, dass die amortisierte Zeit am Ende hier,
gerade O von D von Ennis.
Wir müssen also primär wissen, was dieser maximale Knotengrad ist,
um die Laufzeit korrekt abschätzen zu können.
Ja, dann haben wir uns das D-Quiz Key auch angeschaut.
Das schneidet Knoten heraus und macht das kaskadierend. Erhöht damit
die Anzahl der Wurzeln in der Wurzelliste. Da würde man sagen,
das erhöht natürlich auch den Aufwand für die X-Track-Min-Operation,
die ja diese Wurzeln alle wieder zusammenführen muss.
Auf der anderen Seite haben wir eine amortisierte Analyse und wir
haben ja in der Potenzialfunktion die Anzahl der Wurzeln drin.
Und da wird das quasi berücksichtigt und verrechnet
Okay, das war dann die Schnittoperation. Zum Beispiel,
müssen wir auch nicht nochmal angucken. Die amortisierte Analyse hier
auch nicht mehr, die hängt nicht vom Knoten gerade ab.
Das ist eben nur die Extract-Min-Operation. So, hier waren wir in etwa,
wir haben die Extract-Min-Operation und müssen jetzt nur noch wissen,
wie groß ist diese Funktion D von N.
Also wie viele Kinder kann eine Wurzel maximal haben oder
wie viele Kinder kann ein Knoten maximal haben,
wenn im Heep-N-Elemente gespeichert sind. Was wir zeigen müssen,
ist, dass das logerithmisch ist, also oder logerithmisch wächst,
maximal mit der Anzahl der Knoten,
weil dann hätten wir eben die gewünschte Laufzeiteigenschaft,
dass die Extract-Min-Operationen in logerithmischer Zeit amortisiert läuft
An dieser Stelle kommt nun die Beziehung zu den Fibonacci-Zahlen ins
Spiel und die dem Hieb eben den Namen gegeben hat.
Fibonacci-Folge kennen sie sicherlich alle. Die ersten beiden Elemente sind 0
und 1 und die nächsten Elemente entstehen immer durch die Summenbildung.
Ja, das Entscheidende der Fibunacci-Folge ist an dieser Stelle nur,
dass sie ein exponentielles Wachstum hat.
Man kennt das Wachstum genau. Für uns reicht es auszuzeigen,
dass die Caplos zweite Figur Nachi-Zahl größer gleich viel hoch K ist.
Ja, und hier sieht man das exponentielle Wachstum. Und das Vieh hier,
Das ist in diesem Fall 1,6,18, das ist der goldene Schnitt,
das spielt aber auch im Folgenden keine große Rolle.
Das Entscheidende ist nur, dass es hier exponentiell ist.
Gut, so, wie können wir jetzt zeigen,
dass dieser Grad eben beschränkt ist von so einem Knoten?
Das machen wir in zwei Schritten. Zunächst einmal gucken wir
uns mal die Liste all der Kinder an,
die ein Knoten X haben kann.
Und so ein Knoten X hat jetzt also eine ganze Menge
von Kindern,
die werden jetzt durchnummeriert
Ja. Und bis zu irgendeinem YK, ja. Oh,
das wollen wir natürlich abschätzen. Das ist der Grad,
den der Knoten hat. Jetzt gucken wir uns aber zunächst mal die Kinder an,
dieser Knoten X, der hätte jetzt den Grad K,
ja, weil er K-Kinder hat. Was wir zeigen können,
ist, wir geben denen jetzt eine Reihenfolge diesen Knoten,
also im Fibunacci-Hieb sind die Kinder ja nicht geordnet im Vergleich zum Vinumialhieb,
wo die der Größe nach sortiert sind.
Hier sind sie nicht geordnet, die sind willkürlich,
aber wir können ihnen für die Analyse,
können wir den Kindern eine Reihenfolge geben und wir sagen,
diese Reihen Folge, das entspricht der chronologischen Reihenfolge im Laufe des Algorithmus,
wie diese Knoten zu Kindern von dem X geworden sind.
Ja, so sortieren wir die jetzt einfach mal. Okay,
dann können wir zeigen,
dass der Grad des ersten Knotenziel größer gleich null ist,
was trivial ist, der Grad muss ja größer gleich null sein,
aber wir können insbesondere zeigen,
dass der Grad für die Kinder hier weiter hinten immer größer gleich i minus zwei ist.
Und genau, so wird es ja ein i.deck,
ist immer größer, gleich i minus zwei.
Und genau daraus können wir jetzt etwas später ableiten,
wie groß der Baum im Verhältnis zum Knotengrad ist
So, warum gilt diese Eigenschaft hier? Die kann man
sich wie folgt überlegen. Wir haben ja den
chronologischen Prozess, wie diese Knoten Kinder werden. Ja,
und wenn man sich jetzt mal einen Knoten raussucht,
der hier irgendwo ist, dann sieht die Situation so aus.
Wir haben also hier die ersten, die ersten i minus
1 vielen Kinder, die waren schon da.
Und jetzt kommt das Ite-Kind dazu. Zu diesem Zeitpunkt hier
wissen wir schon, was der Knotengrad ist von diesem Knoten X.
Das muss nämlich i minus 1 sein,
weil der hat ja schon diese i minus 1 Kinder
Vielleicht ist der Knotengrad sogar größer als ihm minus eins,
weil wir an dieser Stelle ja nur noch die Kinder betrachten,
die wir ganz am Ende sehen.
Es könnte ja sein, dass hier Kinder dabei waren,
die wir dann irgendwann mal gelöscht haben.
Aber diese Kinder hat er auf alle Fälle. Okay,
deswegen ist der Grad dieses Knotens größer gleich i minus eins.
Jetzt ist es so, jetzt kommt das neue Kind dazu.
Hier wird also ein Baum da drunter gehängt und zwar der mit der Wurzel YI.
Der kommt dazu. Und zu diesem Zeitpunkt sind die gerade dieser beiden Knoten gleich,
weil nur dann würde dem Consolidate Schritt dieses Linking passieren und
die beiden Knoten würden einander zugeordnet und das würde ein Kind.
Und damit ist eben der Knotengrad des I-minus ersten Kindes auch größer gleich I-minus eins.
Okay, so, jetzt könnte aber noch was passieren.
Und zwar haben wir ja noch diese Markierungsgeschichte und wir wissen ja,
dass wir Kinder auch abschneiden in dem Augenblick,
wo zum Beispiel ein Decrease Key passiert.
Aber wir wissen, dass wir maximal ein Kind abschneiden,
weil wenn wir das zweite Kind abschneiden,
dann wird ein Knoten zu Wurzeln,
dann ist er da nicht mehr da drunter.
Das bedeutet, dass wir eins in der Abschätzung verlieren und
dann haben wir die Schranke dass die resultierende Schranke hier,
dass der Knotengrad YI größer gleich i-2 ist.
Und das brauchen. So,
und wie kommen wir jetzt aus dieser Schranke ableiten,
wie das Verhältnis von Anzahl Knoten zu Knotengrad ist?
Das ist der nächste Schritt. Und da verwenden,
überlegen wir uns zunächst einmal einen Begriff für die Schranke.
Wir sagen für einen Knoten X,
bezeichnen wir immer mit Size,
einfach die Anzahl der Knoten,
die unter so einem Baum
mit Wurzel X liegen
Und dafür definieren wir dann eine Schranke und das ist SK.
Das ist also die entsprechende Mindestgröße, die so einen Knoten hat.
So, und dann können wir die beiden Dinge in Beziehung zueinandersetzen.
Die Anzahl der Kinder hier, dadurch, dass wir quasi hier
die Anzahl der Kinder kennen, können wir das rekusiv anwenden.
Dann kennen wir auch die Anzahl der Kinder, der Kinder und so weiter.
Und damit die Größe abschätzen. Das machen wir hier.
Wenn X also ein Knoten ist im Fibunachi-Hieb,
der hat den Grad K, dann gilt,
dass die Größe, also die Anzahl der Knoten,
die wir unter X irgendwo speichern,
größer gleich SK ist
Das ist klar, das ist nur eine Definitionsfrage.
SK ist ja gerade, haben wir ja gerade definiert,
als die Mindestgröße, die so einen Knoten haben muss.
Und was wir jetzt zeigen werden, ist, dass genau dieses SK,
diese Mindestgröße größer gleich der Caplus zweiten Fibunacci-Zahl ist.
Okay, also per Definition gilt die Größe eines Knotens ist
größer gleich der Mindestgröße eines Knotens mit K vielen Kindern.
Das ist einfach nur, so haben wir das definiert.
Und diese Mindestgröße, die wächst natürlich auch,
das ist eine steigende Funktion,
das heißt SK plus eins
ist größer gleich SK
So, jetzt haben wir wieder die Folge der Kinder und
wir wissen jetzt für jedes Kind, wie viele,
was für ein Gratik das Kind hat, ja.
Also das ist genau diese Schranke, die wir eben gerade
schon nachgewiesen haben, ja. Jetzt können wir berechnen,
wie viele Knoten wir haben, indem wir einfach sagen,
okay, also die Anzahl der Knoten,
die wir mindestens haben auf Level K,
also dieser Knoten hier, Der hat ja Grad K.
Das ist erstmal ein Knoten, das ist der Knoten X
selber hier. Und dann können wir überlegen, okay,
wir können uns für jeden Knoten überlegen, wie viele Kinder oder wie viele,
was für eine Größe repräsentiert dieser Knoten hier noch, dieses Kind.
Und dann würden wir sagen, aha,
das ist jetzt also nichts weiter als die Summe über diese ganzen Kinder hier,
die läuft jetzt von I gleich 1 bis K.
Und dann haben wir die Abschätzung, also S für die Anzahl,
die Größe, die Anzahl der Knoten,
die jetzt einen Baum hat mit dieser Wurzel und der Grad von diesem Knoten,
den haben wir gerade abgeschätzt, das wäre genau Genau,
also erstmal YI.
Die Gree, ja, also der Grad in der Knoten hat.
So, diese Summe hier, die können wir jetzt
ganz leicht vereinfachen. Wir nehmen das erste Summen-Element raus,
dann ist das 2 plus und die Summe läuft von ILAI 2 bis K,
S-Y-I.degree, also der Grad von dem Eaten-Knoten.
Und dann benutzen wir hierfür unsere Abschätzung und sagen,
dass der Knoten gerade hier mindestens minus 2 ist,
ja, und die Funktion, die S-Funktion des Monotons steigt,
also wenn der kleiner ist,
wenn das nur in die richtige Richtung abschätzen,
das heißt,
das ist größer gleich 2 plus die Summe von ILAI 2
bis K,
S-I minus
Haben wir eine Beziehung hier stehen, die genau so aussieht,
wie das, was wir gerade schon,
oder was wir vorher für die Figurenatschi-Zahlen gesehen haben.
Das heißt, das SK, hier wird definiert,
zumindest in der oberen Schranke,
durch zwei plus die Summe von ILAX 2 bis KSI minus zwei.
Wenn wir uns die Figurenatschi-Zahlen angucken, dann sehen wir hier ein ganz ähnliche Funktionen.
Die Carplus-Seite für Bunachi, die Zahl, können wir berechnen,
durch eins plus die Summe über die vorherigen Figurenatschi-Zahlen.
So sieht es hier entsprechend eben auch aus. Dementsprechend ist
diese Analogie da und wir können dann per Induktion zeigen,
dass in der Tat dieses SK durch die durch die Fibunachi-Zahlen beschränkt ist.
Das SK ist jetzt also größer gleich.
Diese Summe hier, das haben wir gerade schon gezeigt.
Und das SI minus zwei ist größer als die IT Fibunachi-Zahl.
Das macht man dann mit einem Induktionsbeweis. Und wenn man das gezeigt hat,
dann steht hier natürlich eins plus ILACH 0 bis KFI.
Und dann können wir hier entsprechend die Definition einsetzen,
haben wir die Carplus zweite Fibunachi-Zahl. Also diese Zeile muss man
per Induktion zeigen, aber das ist ja ganz einfach.
Das setzt man einfach die Werte für K gleich eins und zwei
erstmal ein und dann kann man für K von dem nüchst höheren K,
auf die darunter liegenden K-Werte einfach schließen. Okay,
und damit haben wir gezeigt, dass die Anzahl der Knoten,
die unter einem Baum hängen, wo die Wurzelgrad K hat,
größer gleich der Caplos 2. Figurnatsch die Zahl ist,
und weil die Figurnatsch die Zahlen exponentiell wachsen,
haben wir gezeigt,
dass das Verhältnis von Anzahlknoten zu Grad an der Wurzel logerithmisch ist.
Das sagt dieses abschließende Corona, das heißt der maximale Knoten,
gerade im Figurnatschi-Hiebs, wer ist mit DVN bezeichnet worden in unserer Analyse
Und das liegt in O von Login.
Und das folgt sofort aus dem exponentiellen Wachstum der Fibonacci-Zahlen.
Also das N ist größer gleich der Anzahl der Knoten an einem bestimmten Knoten X,
also die unter einem Baum mit Wurzel X liegen.
Ja, weil, naja, da kann natürlich immer
nur ein Teil der gesamten Knotenmenge liegen.
Und das ist aber größer als die Caplos zweite Fibonacci-Zahl.
Und das ist 4OK. Und dann können wir das umformen und sehen,
dass K kleiner gleich Logorithmus zur Basis Vieh von Ennis im O-Kalkül
spielt die Basis keine Rolle
Und das D von N ist der maximal mögliche Knotengrad.
Wir müssen also maximieren über alle möglichen Knoten X hier,
den Knotengrad und dann haben wir genau das,
was wir gesucht haben, nämlich dass der Knoten,
gerade der maximal mögliche Knotengrad, logorhythmisch ist.
Okay, und das muss man nur noch vorne einsetzen,
um dann zu sehen, dass das Verfahren Logorithmischel-Aufzeit hat.
So ein bisschen, ja,
wenn man dann an die Realisierung denkt,
dann ist es an dieser Stelle so ein bisschen unbefriedigend,
weil,
naja, wir haben gesehen,
im Algorithmus brauchen wir tatsächlich das D von
Also wir müssen ja, wir brauchen ja einen Array mit
D von N vielen Elementen. Das heißt,
eigentlich müsste man, um es wirklich zu implementieren,
hier eine genauere Abschätzung von dem D von Innen haben,
um dann wirklich das Array auch initialisieren zu können.
Aber aus rein theoretischer Betrachtungsweise ist das natürlich völlig ausreichend zu zeigen,
dass es so ist. Das heißt, es gibt irgendeine Konstante,
die wir jetzt nicht kennen, aber mit der wir abschätzen können,
dass das D von N kleiner ist, als zehnmal Login.
Ja, das war sozusagen das Ende der Analyse des
Fibonacci Heeps
Und ja, über den kann man jetzt ein bisschen philosophieren.
Ja, was man hier wirklich mit ihm tun kann.
Was wir gesehen haben, ist, wir haben ja die
Laufzeitschranken gesehen. Wir haben gesehen, okay, amortisiert,
die Crease-Key geht in Ovon 1 und die Extract-Min-Funktion bleibt Log-N.
Und damit können wir tatsächlich im O-Kalkül die Laufzeitschranke für Diextra
und Prim reduzieren. Auf V-Log V plus E. Das ist
erstmal die Konsequenz von der reinen theoretischen Betrachtungsweise
Von der praktischen Seite her stellt sich jetzt natürlich die Frage,
sollte man das jetzt implementieren? Ist das die super Lösung? Ja,
um quasi ein besonders schnelles, kürzeste Wegeverfahren zu realisieren.
Dann wissen wir bereits, ja, okay, das ist also eine Angabe im Okalkül.
Da sind die konstanten versteckt. Und es könnte sein,
dass sich hinter dem O von Login bei dem Fibonacci-Hieb eine wie höhere Konstante versteckt,
als bei dem normalen Binärenhieb, den wir gesehen haben.
Das würde dazu führen, dass wir sehr große Eingaben brauchen,
ich das tatsächlich amortisiert. Das ist das Erste,
was man im Kopf haben muss und in der Tat ist es so.
Also der Figunacci hat wesentlich höhere Konstanten. Das Nächste,
was man dann sehen muss, ist der Laufzeitvorteil,
kommt nur dann zustande, wenn wir einen dichten Grafen haben.
Solange wir also einen Grafen haben mit einem begrenzten Knotengrad,
ist ja die Anzahl der Kanten auch nur eine konstante,
mal die Anzahl der Knoten.
Also wenn wir irgendwie maximal zehn Kanten pro Knoten haben,
dann haben wir eben auch nur maximal zehnmal so viele Kanten,
wie wir Knoten haben.
Und in dem Augenblick würde der Vorteil
auch wieder verschwinden
Ja, weil E-LogV dann im Okalkül das Gleiche ist wie V-LogV,
weil E und V,
also die Anzahl der Kanten und die Anzahl der Knoten,
ja auch nur über eine Konstante zueinander in Beziehung stehen.
Also da wird es sich eben auch nicht lohnen. Und selbst wenn das nicht gilt,
stellt sich natürlich die Frage, ob es wirklich besser ist.
Ja, und ob wir wirklich gezeigt haben, dass es besser ist.
Und da muss man auch aufpassen, denn man neigt dazu,
dass Okalkül immer als eine relativ präzise Schranke zu interpretieren,
für die Laufzeit. Das Einzige, was wir gezeigt haben,
ist ja, Wir haben den binären Hieb gehabt,
ja, und da haben wir gezeigt,
die Laufzeit für Deecrease Man war, oh von Login,
ja, und dann haben wir den Syrunachi-Heeb gehabt und dann haben wir gesehen,
okay, die Laufzeit von der Decrease-Min-Operation oder Decrease-Key-Operation war amortisiert,
oh von 1, ja,
also es ist jetzt mal die Criskey als ein Beispiel.
So, offensichtlich haben wir hier eine präzisere Schranke
Ja, als hier, also eine bessere,
eine engere Schranke, ob es enger ist,
wissen wir nicht, aber zumindest mit einem niedrigeren Wert.
Aber diese Log-End-Schranke für den binären Hieb ist eine obere Schranke,
so wie das Okalkül immer als obere Schranke eingesetzt wird.
Die reale Laufzeit, der Diquise-Key-Operation für den binären hieb,
die kennen wir ja gar nicht. Wir kennen nur, wir wissen nur,
dass Log-End eine obere Schranke ist, ja. So,
das heißt, wenn wir Algorithmen haben,
wo wir im Okalkül obere Schranken haben,
dann haben wir natürlich hier die Garantie, hier unter O von 1,
also das ist so ein konstantes Laufzeitverhalten sehen, ja,
die haben wir hier nicht.
Aber ob die reale Laufzeit wirklich besser ist, das sehen wir hier nicht.
Vielleicht ist einfach nur die Abschätzung dieser Schranke nicht gut genug, ja.
Vielleicht müsste man sich nur mal hinsetzen und könnte mit einer viel,
viel, viel besseren Analyse des einfachen binären Hiebs auch zeigen,
dass die Laufzeit geringer ist als Logorithmus von N,
ja, weil es eben nur eine Schranke ist.
Einen echten Beweis, dass das besser ist als das,
hätten wir nur, wenn wir zeigen könnten,
dass die D-Crease-Key-Operation So ein Menger von Login-Zeit hat.
Dass sie mindestens so stark wächst, wie die Logarithmenfunktion.
Dann wäre es ganz klar, dann wüssten wir ganz klar,
okay, also das ist die bessere Funktion.
Das hat aber bisher noch niemand gezeigt. Das wissen wir
nicht. Insofern ist es so, der Fibunachi-Hieb hat nachgewiesen,
dass wir tatsächlich bessere Laufzeitschranken erreichen können für eine kürzeste Wegesuche.
Aber deswegen wissen wir in der Praxis noch nicht hundertprozentig,
ob es wirklich das bessere Verfahren ist,
auch für ganz große N wissen wir das nicht
Und ich sage das hier nochmal so,
weil es in der Informatik so üblich ist,
also das O-Kalkül ist ja sozusagen,
das geht ja sozusagen irgendwann in den Sprachgebrauch über.
Und es ist so üblich, wenn wir von O von Log-End sprechen,
dass wir es tatsächlich gleichsetzen mit dem Laufzeitbedarf.
Und dass man in dem Augenblick vergisst, dass es eigentlich nur eine obere Schranke ist.
Und dass es hier eben nicht unerheblich. Ja,
aber trotzdem kann man von dem Fibonacci-Hieb eine Menge lernen.
Also was ich richtig toll finde an dieser Struktur ist,
dass man sieht, Also, wenn man Datenstrukturen baut in der Praxis,
dann kommt man meistens zu dem Schluss,
wenn man noch zusätzliche Struktur integriert,
dann kann man Operationen schneller machen.
Wenn ich noch ein paar Pointer einbaue, wenn ich noch mehr,
wenn ich noch die Objekte in der speziellen Art sortiere,
dann wird eine Zugriffsoperation in der Regel schneller und effizienter.
Und auf der anderen Seite hat man immer so einen Gegenpol,
weil je mehr Struktur man integriert in eine Datenstruktur,
je mehr man dafür sorgt, dass man schnelle Zugriffsoperationen hat,
umso mehr muss man natürlich auch investieren,
um so
eine Struktur aufrechtzuerhalten
Und der Fibrunachi-Hieb ist ein schönes Beispiel dafür,
wie man durch den Abbau von Struktur, ja,
wir verzichten ja im Vergleich zum binären Hieb oder zum minumialen Hieb,
muss man sagen, auf Struktur.
Wir akzeptieren, dass die Wurzeln nicht mehr in einer festen Reihenfolge sind zum Beispiel.
Ja, wir akzeptieren, dass da Knoten nicht mehr den
vollständigen Grad haben, sondern dass die Kinder verloren haben.
Wir reduzieren den Grad an Struktur, den wir haben, an Ordnung.
Und es resultiert am Ende eine effizientere Datenstruktur. Und das ist eine für mich,
oder aus meiner Perspektive eine ganz wichtige Erkenntnis,
dass es nicht immer nur darum geht,
wenn man effiziente Datenstrukturen hat, dass man versucht,
mehr Struktur zu integrieren, um schnellere Zugriffe zu realisieren,
dass man immer auch im Kopf haben muss,
man muss diese Strukturen eben auch instandhalten.
Und dass das natürlich auch Zeit kostet. So, als letztes Beispiel,
im Bereich Datenstrukturen, möchte ich Ihnen gerne noch die Treeps zeigen,
weil das ist eigentlich eine ganz einfache Struktur.
Aber ich finde die sehr nützlich und praktisch für die Praxis.
Und Sie werden jetzt sehen, wir gehen jetzt wieder ein bisschen
zurück zu diesen Suchbäumen und werden aber auch Heeps noch mitbetrachten.
Und wir können diese beiden Dinge eigentlich vereinen und damit quasi sehr präzise,
steuerbare Suchbäume konstruieren. Also was wir machen möchten,
ist, wir hätten gerne,
sie müssen jetzt den beim Hieb wieder zurückschalten auf dem normalen Binärenhieb,
wo wir also nach wie vor wieder zwei Kinder haben,
sodass, so wie wir das schon ganz von Anfang an hatten.
Und was wir jetzt gerne tun möchten, ist,
wir würden gerne mal einen Baum bauen,
der sowohl die Hieb-Eigenschaft besitzt als auch eine Suchbaum-Eigenschaft.
Also wir haben Knoten und die Knoten haben wieder maximal zwei Kinder,
so wie wir das kennen. Und diese Struktur,
die wir hier sehen, soll auf der einen Seite ein Suchbaum sein.
Das bedeutet, wir wissen, wenn wir so einen Knoten anschauen,
dann sind links die kleineren Elemente gespeichert und rechts die größeren Elemente.
Auf der anderen Seite soll es ein Hieb sein,
also eine Suchbaumeigenschaft sagt was aus, dazwischen was links und
was rechts steht und eine Hieb-Eigenschaft sagt etwas darüber raus,
was oben und was unten steht.
Also in dieser Richtung hätten wir gerne Hieb Heep.
Und in dieser Richtung hätten wir gerne super. So,
das würden wir gerne bauen oder zumindest mal darüber nachdenken,
ob das überhaupt geht. Wir gehen jetzt davon aus,
dass unsere Knoten hier zwei Werte haben, also zwei Attribute,
die heißen Key und die heißen Prio.
Key ist sozusagen der Schlüssel, nach dem wir suchen.
Und bezüglich Key gucken wir immer, ob wir nach links
oder nach rechts verzweigen, müssen wir wenden.
Objekt suchen. So, und dann
Er hat so einen Knoten, auch nochmal die Eigenschaft Prio.
Und das ist eine Priorität und die Priorität sagt uns,
wie weit oben im Baum so ein Knoten stehen soll.
Und wenn sie sich jetzt erinnern an die Splaytrees,
an die selbst organisierenden Bäume,
dann war ja ein ganz wichtiges Argument, dass man gesagt hat,
naja, auf ein Element, was man zugegriffen hat,
greift man vielleicht in der näheren Zukunft wieder zu.
Und deswegen sollte das den Baum möglichst weit oben stehen.
Und der Baum sorgt selber dafür, dass das passiert.
Okay, aber das muss ja jetzt nicht unbedingt so sein.
Viel wahrscheinlicher ist es vielleicht, dass wir aus der Benutzung einer Software,
auf einer längeren Historie heraus, eine Zugriffswahrscheinlichkeit ableiten können.
Ich hatte, als Beispiel hatte ich da gesagt,
naja, nehmen wir mal einen Buchkatalog,
zum Beispiel von einem Online-Buchhändler und wenn man dann eben den Bestseller anfragt,
dann steht er danach weiter oben im Suchbaum und wenn der Nächste wieder den Bestseller anfragt,
dann kriegt er eine schnellere Antwort.
Wenn jetzt aber dann der Übernächste ein Buch anfragt,
was total selten angefragt wird, dann greift er auf etwas zu,
was weit unten schnickt im Baum,
das wandert dann nach oben,
was vielleicht ja gar nicht unbedingt nötig wäre,
weil wir schon wissen,
also längerfristig gesehen war das
eine seltene Anfrage
Also mal angenommen, wir können über einen längeren Zeitraum protokollieren,
wie häufig Elemente angefragt werden. Dann können wir daraus eine
Zugriffswahrscheinlichkeit ableiten und eine Zugriffswahrscheinlichkeit können wir überführen in eine Priorität.
Und dann können wir in dem Baum so organisieren,
dass Knoten mit einer hohen Wahrscheinlichkeit des Zugriffs eine entsprechende Priorität
bekommen und weit oben im Baum stehen.
So, so würden wir das gerne machen. Und unser
Warm erfüllt also jetzt beide Eigenschaften, Hieb-Eigenschaft und Suchbaumeigenschaft.
Und das bedeutet nichts weiter, als das für alle Knoten gilt,
Wenn X ein rechtes Kind ist, seines Vorgängers,
dann ist der Schlüsselwert kleiner als der des Vorgängers.
Und wenn X ein, Entschuldigung, das war falsch,
wenn X ein rechtes Kind ist, seines Vorgängers,
ist der Schlüsselwert größer als der von seinem Vorgänger.
Und wenn es ein linkes Kind ist,
dann ist der Schlüsselwert kleiner als der seines Vorgängers.
Das ist die Suchbaumeigenschaft. Und die dritte Eigenschaft ist,
dass für jeden Knoten gilt, dass seine Priorität größer ist
als die Priorität des Elternknotens.
Das mit dem größer ist jetzt wieder vielleicht ein bisschen verwirrend.
Also wir sortieren das hier als ein Min-Hieb.
Das heißt, Ein MINF-Minhieb. Das heißt,
wir sortieren die so, dass kleine Prioritätswerte weiter oben sind.
Okay, gut. Die Frage ist jetzt erstmal,
geht das überhaupt? Vielleicht geht das gar nicht.
Also vielleicht können wir gar nicht die Knoten,
wenn wir beliebige Schlüssel und Prioritäten setzen, so organisieren.
Vielleicht widersprechen sich einfach die Hieb und die Suchbaum-Eigenschaft.
Das ist nicht so, wie das folgende Dilemma zeigt.
Wir können also jede Konstellation,
also jede Sammlung von Daten mit x-beliebigen Schlüsseln und Prioritätswerten
in so
einen Baum überführen
Ja. Das ist das Slammer. Also wir haben also
einen Datensatz, X1 bis XN und jeder Datenpunkt,
jedes Objekt hat also einen Schlüssel und eine Priorität.
Und dann gibt es einen endelementigen Suchbaum,
also der hat halt für jedes Element im Datensatz einen Eintrag hier,
der ein Suchbaum ist bezüglich der Key-Eigenschaft,
des Key-Attributs und der die Hieb-Bedingungen bezüglich des Prio-Attributs erfüllt.
So, wie macht man das? Das geht ganz leicht
über einen Induktionsbeweis. Wir gehen am Anfang davon aus,
wir haben nur ein einziges Datenelement.
Also wir haben nur einen Warm, der aus einem Knoten
besteht und dann ist offensichtlich sind dann alle Eigenschaften erfüllt.
Da müssen wir nichts zeigen. Und jetzt haben wir einen
größeren Baum mit vielen Knoten. Dann gehen wir wie folgt vor
. Wir suchen uns den Knoten raus mit minimaler Priorität.
Und das ist entsprechend die Wurzel. Also das ist hier
der Knoten-I mit der Eigenschaft, dass das AI.prio minimal ist.
Also der muss die Wurzel darstellen in dem Baum, das wissen wir. Und wir wissen auch,
dass für alle anderen Knoten können wir jetzt zwei Mengen bilden,
eine linke Menge und eine rechte Menge anhand der Suchbaum-Eigenschaft,
weil wir wissen, alle anderen Knoten,
die müssen jetzt irgendwie hier drunter und wir dürfen die natürlich nicht beliebig da drunter bauen,
sondern wir wissen, die, die mit einem kleineren Schlüssel müssen links sein,
die mit einem größeren Schlüssel müssen rechts sein,
ja.
Deswegen bauen wir diese zwei Mengen, L und R,
L sind also alle Elemente aus der Menge A mit der Eigenschaft,
dass der Schlüssel kleiner gleich der Schlüssel XE ist,
also hier oben drin.
Und er ist die andere Menge. Sind die,
die größer sind? Aus L müssen wir natürlich,
weil das hier ein kleiner gleich ist,
den Schlüssel X oder das Datenelement X,
die müssen wir rausnehmen.
Das haben wir ja schon in die Wurzel gepackt hier
Insofern brauchen wir das nicht mehr in der Menge. So,
jetzt wissen wir, weil wir die Wurzel haben,
müssen sowohl L als auch er bezüglich der Anzahl der Elemente
kleiner sein, als die Ursprungsmenge A.
Und dann können wir unsere Induktionsannahme einsetzen. Das ist dafür
so eine Baumstruktur gibt. Ja, und das wäre dann
der Suchbaum TL. Und den können wir dann hier drunter hängen.
Und hier haben wir den Superum TR, der für die
rechte Menge steht. Und damit haben wir quasi gezeigt,
dass wir einen Baum Tee konstruieren können für diese Menge
A. Ja, der entsprechend alle Elemente enthält.
Ein ganz einfaches Argument
Okay, diese Struktur, die wir hier sehen,
die bezeichnen wir als Treep. Das ist ein Kunstwort,
das steht für Tree and Heep, ja, das ist ein Treep.
Und die hat genau jetzt diese Eigenschaften. Knoten haben einen
Schlüssel und eine Priorität und die Knoten sind so arrangiert,
dass wir Priorität berücksichtigen in der Höhe im Baum und den Schlüssel bezüglich der Reihenfolge der Kinder.
Okay, hier ist ein Beispiel für so einen Treep.
Sie sehen, vorne steht hier immer der Key, ja,
also hier steht zwei, links ist eins, rechts ist acht,
drei bis kleiner, sechs ist dann größer als vier.
Fünf ist kleiner als sechs und so weiter.
Also vorne steht immer der Schlüssel und hinten steht die Priorität.
Also hinter dem Komma steht die Priorität, also der hat Priorität eins,
der hier hat zwei, der Knoten hat drei,
der hat vier, der hat fünf und so weiter.
Und sie sehen, die sind jetzt nicht geordnet nach der Priorität,
sondern das einzige, was wir haben, wissen ist,
dass ein Knoten immer einen größeren Prioritätswert hat als der Vorgänger.
So, okay, das wäre unser Suchbaum und Suchbäume
sind natürlich irgendwie nur so so richtig toll,
wenn wir sie irgendwie auch, wenn sie balanciert sind.
Und dann stellt sich natürlich die Frage, können wir jetzt
hier irgendwie auch was balancieren? Können wir jetzt sagen,
wir rotieren mal ein bisschen um einen balancierten Baum zu erhalten?
Das geht nicht. Ja, wir können diesen Baum
nicht mehr balancieren. Wir haben quasi den Freiheitsgrad,
den wir typischerweise noch haben in den Suchbäumen, wo wir sagen können,
wir können das nach links oder nach rechts schieben,
den haben wir aufgegeben oder den haben wir verwendet für etwas anderes.
Den haben wir verwendet dafür, dass wir die Priorität berücksichtigen können.
Das kann man relativ schnell sehen, mal angenommen,
wir haben Datensatz A mit eindeutigen Schlüsseln.
Also die Keys sind eindeutig. Also wir haben keine zwei Schlüssel,
die gleich sind. Wenn wir so eine Situation haben,
ach so, die Prioritäten sind auch eindeutig,
also sowohl die Prioritäten als auch die Schlüssel sind eindeutig.
Wenn wir so eine Situation haben, dann ist auch
der Treep, der diese Datenmenge speichert, eindeutig.
Wir haben keine Alternativen. Es gibt nur einen Treep,
der dieses Datensatz, diesen Datensatz A am Ende speichert.
Das können wir wieder genauso zeigen, wie eben gerade der Beweis,
wenn wir einen Knoten haben, ist die Aussage trivial,
wenn wir viele Knoten haben, dann existiert eine eindeutige Wurzel.
Das ist der Knoten mit minimaler Priorität. Da gibt es ja
jetzt nur noch einen, weil jede Priorität kommt nur einmal vor.
Die Mengen L und R sind eindeutig definiert,
weil es gibt ja keine gleichen Schlüssel. Das heißt,
entweder ist der Schlüssel kleiner oder er ist größer,
dann kommt er in die linke oder in die rechte Menge.
Und wenn wir das als Induktionsbeweis formulieren, wissen wir,
dass es dann Treeps auf der linken und auf der rechten Seite gibt,
die die Eigenschaften erfüllen.
Und die sind natürlich auch dann eindeutig. Und der Baum,
der dann entsteht, ist dann ebenfalls eindeutig.
Das heißt, wenn Schlüssel und Prioritäten eindeutig sind, Treeps eindeutig.
Und damit bedeutet das, wir haben nur einen Treep,
der eine Datenmenge repräsentieren kann. Und dann können wir natürlich auch nicht balancieren,
weil Rotieren bedeutet ja nur einen Suchbaum durch einen anderen validen Suchbaum zu ersetzen,
der die gleiche Datenmenge repräsentiert, aber eben, naja, niedrigere Höhen hat.
Aber wenn es hier nur einen gibt, können wir den natürlich durch nichts ersetzen.
Ja, das heißt, balancieren können wir nicht.
Wir müssen also dann mit der Struktur leben,
so wie sie rauskommt, wie sie durch die Prioritäten vorgegeben werden.
Wie können wir denn jetzt einfügen oder löschen
Also, wir können nicht so was machen wie hiepify,
ja, weil wenn wir eine hiepify-Operation ausführen,
dann vertauschen wir Elemente mit Vorgängern. Das geht nicht,
ja, weil wir dadurch natürlich die Suchbaumeigenschaft potenziell verletzen.
Also, sowas können wir nicht machen. Aber es gibt
einen anderen Trick, um diese Struktur zu manipulieren.
Und zwar können wir die Rotation, die wir bisher zum
Balancieren eingesetzt haben, benutzen, um die Treep-Eigenschaften wiederherzustellen.
Wir nutzen sie jetzt nur in einem anderen Kontext,
und zwar in einen Kontext,
den sie eigentlich schon kennen
Indem wir Knoten bezüglich der Priorität in der Höhe adjustieren.
Das haben die Splay-Trees auch gemacht mit der Move-to-Roo-Teuristik,
die ja sozusagen die Grundlage ist von dem Splay-Tree,
haben wir ja Rotationen eingesetzt,
um Knoten von unten nach oben zu verschieben.
Wenn wir jetzt also in einem Baum die Prioritäten verändern,
können wir durch Rotationsoperationen den Knoten nach oben verlagern.
Jedenfalls auch nach unten,
aber wir brauchen es eigentlich
nur nach oben
So, und auf der Basis können wir ganz leicht eine Einfüge-Operation definieren.
Wir machen also eine Einfüge-Operation, genau wie in jedem anderen
unbalancierten Suchbaum auch. Wir suchen als erstes die Position,
wo das Element bezüglich des Suchschlüssels hingehört. Ja,
das passiert in dem ersten Teil dieses Pseudo-Kurze für das Insight.
Wir haben also dann hier, wir starten an der Wurzel,
Y wird immer der Vorgänger sein von dem X,
solange X ungleich nie ist und wir den Schlüssel,
den wir jetzt neu einfügen wollen,
das ist Z.Key,
nicht bereits in dem Suchbaum haben
An der Position X. Gehen wir jetzt eine Ebene runter.
Y wird also zu X gesetzt. Wir gucken uns den
Knoten Z an, vergleichen den mit X. Und wenn der
kleiner ist, dann müssen wir in die linke Hälfte verzweigen.
Anderenfalls müssen wir in die rechte Hälfte verzweigen. Wenn der
Schlüssel gleich ist, dann stoppen wir an dieser Stelle,
weil wir einen gleichen Schlüssel nicht zweifach einfügen.
Das ist jetzt so gemacht. Wir können natürlich auch gleiche Schlüssel einfügen,
dann müssen wir es ein bisschen anders bauen hier.
So, wenn diese Wahlschleife durchgelaufen ist,
dann ist X auf alle Fälle, ja,
wahrscheinlich nie,
das hängt hier unten irgendwo
Und Y war der letzte Knoten, den wir gesehen haben,
bevor wir dann hier auf den Nil-Knoten oder auf das Nil verzweigt haben.
Wenn X jetzt gleich nie wisst, dann wissen wir,
dass an dieser Stelle der Knoten Y der neue Vorgänger ist.
Also an der Stelle können wir jetzt den Knoten-Set einfügen. Ja,
und z.parent wird dann entsprechend das Y. Und da müssen wir noch gucken,
ob das jetzt das linke oder das rechte Kind war.
Und das können wir nochmal machen, indem wir den Schlüsselwert
an der Position Z mit denen von Y vergleichen.
Und wenn der Schlüsselwert von Z kleiner ist,
dann wird es ein linkes Kind und sonst wird es halt
ein rechtes Kind
Okay, anderenfalls, wenn das X ungleich nied ist,
dann haben wir aufgehört, weil die Schlüssel gleich waren und dann,
ja, dann machen wir eine Fehlermeldung, zu sagen,
das Element ist da schon drin,
das können wir jetzt nicht mehr einfügen.
So, jetzt haben wir den Knoten hier eingefügt.
Nur anhand der Suchbaumeigenschaft, ja, der ist da jetzt drin,
der hat aber ja auch noch eine Priorität, ja,
die steht ja hier noch da drunter.
Und die haben wir jetzt noch gar nicht berücksichtigt und
die Priorität muss ja mit der Minhieb-Eigenschaft im Einklang sein.
Ja, das müssen wir jetzt noch reparieren.
Und das machen wir in einer Operation
Die analog zum Hi-Bify diesen Knoten nach oben bewegt,
aber sie kann das eben nicht durch Austauschoperationen machen.
Also Hi-Bify würde ja nur vergleichen und die Knoten vertauschen,
bis wir dann an die richtige Position gekommen sind.
Das geht nicht, weil wir damit die Suchbaumeigenschaft verletzen.
Aber wir können rotieren. Und das ist genau das,
was wir machen. Solange also Z noch einen Vorgänger hat und
die Priorität von Z ist kleiner als die Priorität des Vorgängers.
Also kleine Werte, wie gesagt, an die nach oben.
Ja, sind gut. Gucken wir, wenn Z ein rechtes Kind ist,
machen wir eine Linksrotation am Set und rotieren, dann entsprechend so rum.
Und wenn ein Set ein rechtes Kind ist,
dann ein linkes Kind ist, machen wir eine Rechtsrotation.
Auf die Art und Weise würde dann Z schrittweise durch Rotation,
das würden wir dann so rotieren und dann so rotieren und
vielleicht dann nochmal so rotieren und dann würde es irgendwann mal in der Wurzel ankommen und den Baum entsprechend umarrangieren.
Und eine Rotation erhält die Suchbaumeigenschaft, das wissen wir schon lange,
weil damit balancieren wir ja auch,
da muss ja auch die Suchbaumeigenschaft erhalten bleiben.
Und die Rotation sorgt dafür,
dass ein Knoten weiter nach oben rutscht und damit sich das
Verhältnis in der Bezüglich der Priorität vertauscht
Ja, hier ist ein Beispiel, wie das aussieht.
Also wir sind hier runtergelaufen, haben diesen Knoten 7 hier eingefügt, hier unten.
Deswegen sind die beiden Knoten hier mit so einem Sternchen markiert.
Und jetzt würden wir sehen, aha,
der hat hier Priorität 0, der hat Priorität 6,
also ist die Priorität hier eigentlich kleiner, der müsste weiter nach oben.
Wir rotieren also um diese Kante. 7 kommt also nach oben,
6 wird dann linkes Kind und das linke Kind von
dem Knoten bleibt hier, linkes Kind von denen.
Dann ist der Siebener da, wir vergleichen den wieder mit seinem Vorgänger,
der hat Prio 0, der hat Prio 5,
also müssen wir den wieder nach oben rotieren.
Wir machen jetzt also hier eine Linksrotation, der Knoten wandert hier so rüber.
Kommt die Linksrotation, hier ist dann der Knoten 7,
da wird der Knoten 4, wird linkes Kind.
Bei der Rotation, das vormals linke Kind des Knoten 7 hier,
wird dann rechtes Kind von dem Vorgänger und so weiter.
Und dann können wir um diese Achse noch mal rotieren und
sind dann in dem Baum,
dann rotieren wir noch mal nach links und kriegen dann hier diesen resultierenden Baum,
wo dann unser neu eingefügter Knoten tatsächlich auch an der Wurzel ist.
Frage? Nein,
was gemeint ist jetzt die Rotat ab
Also wir eben starten ja einfach Lesbull Chade Z. Und so
wie wir den Routen bis jetzt immer definiert haben,
mit der Knoten, der rotiert wird, nach unten rumtiefen.
Also wenn wir es gesagt haben, Routen Deft von links
, dann ist es jetzt solcher Runde gegangen, Alter.
Ich dachte, wir hatten mal auf einer Full-Evo-Take-Arme definiert,
als Routiere den Vater. Also Roadtate-Up habe ich,
das hätte man hier einfügen können, da haben sie recht,
das wäre sogar ganz schick gewesen vom Algorithmus,
habe ich hier tatsächlich nicht gemacht,
weil ich das hier aus einem,
ja,
im Prinzip hätte man hier statt diesem Code,
hätte man
Roadtate-Up von Set.p schreiben können
Das wäre das Gleiche gewesen. Also Roadtate Up habe ich
damals definiert bei dem Move to Front,
damit man nicht immer diese Fallunterschreibungsscheidung hinschreiben muss.
Damit man einfach sagen kann, je nachdem,
ob es ein rechts ist oder ein linkes Kind ist,
muss ich links oder rechts rotieren.
Das hätte... Sorry, wir haben Roadtate, also hier
steht Routtate ab von N, ist definiert, als rotierem.g.
Das bedeutet ja, nd.gp reportiert, also nach Kunden gebracht.
Ja, genau. Und dann würde hier,
wenn hier, müsste jetzt, wenn hier jetzt Roadtate,
dann müsste ich jetzt hier, da haben sie recht,
dann müsste hier Roadtate up von Z stehen,
ja?
Ja,
oder Left Routtate von Z Coopy
Also, wenn man ein Set da durch Z.P.,
okay, er sitzt, dann macht es auch das.
Okay. Die haben zum Beispiel hier
die Funktion der Budget haben, mit X. Ja.
Und da ist das X-O und das Y wird dann hoch.
Okay. Okay, ich gucke es mir noch mal an.
Das kann sein, dass ich das tatsächlich vertauscht habe.
Ich schaue es mir noch mal an. Das kann sein
. Aber sie wissen, was ich öffne. Okay,
super. Danke. Gut. Das war das Beispiel.
So, können wir in dem Treep auch löschen?
Wir können auch löschen. Das ist auch nicht schwierig. Wir wissen,
dass wir in einem binären Suchbaum, wenn wir löschen wollen,
dafür sorgen müssen, dass ein Knoten Kein, keine zwei Kinder hat.
Er darf nicht zwei Kinder haben, sobald er ein Blatt ist,
können wir ihn ja einfach entfernen.
Wenn ein Knoten nur einen Nachfolger hat, können wir ihn auch entfernen,
indem wir dann den einen Nachfolger zum Kind des Vorgängers von dem Knoten machen,
den wir löschen.
Das ist ja die Art und Weise, wie wir in einem binären Suchbaum löschen.
Wir können nur nicht quasi einen Knoten löschen, der zwei
Kinder hat. Beim normalen Suchbaum erledigt man das dadurch,
dass man einen Knoten ersetzt durch seinen symmetrischen Nachfolger. Ja,
dann läuft man jetzt zum symmetrischen Nachfolger oder Vorgänger,
kann man sich aussuchen, trägt den da ein,
vertauscht die Elemente und dann hat man auf alle Fälle einen Knoten zu löschen,
der nur ein Kind hat, ein oder kein Kind hat.
Und das geht natürlich jetzt auch wieder nicht, weil wir dürfen nicht
einfach Elemente vertauschen, dann zerstören wir wieder die Hieb-Eigenschaft.
Wir können den Knoten aber rotieren und das machen wir mit,
indem wir quasi die Priorität einfach, wenn sie so wollen, manipulieren.
Ja, wir rotieren den Knoten einfach.
Und zwar rotieren wir den abwärts, bisher keine zwei Kinder
mehr hat
Dabei haben wir jetzt dann zwei Möglichkeiten. Wir können ihn
nach links rotieren, aber wir können ihn nach rechts rotieren.
Ja, wenn wir ihn runterrotieren. Wenn wir das machen,
machen wir das so, dass wir die Priorität der beiden Kinder hier berücksichtigen.
Das heißt, wenn die Priorität des linken Knotens kleiner ist als die des Rechtens,
machen wir eine Rechtsrotation und sonst eine Linksrotation.
Ja. Okay, dann rotieren wir den nach unten.
Und wenn wir das getan haben, gucken wir,
ob der Knoten noch ein Kind hat.
Das nennen wir dann X. Also wenn der linke Nachfolger noch existiert,
dann nennen wir den X. Anderenfalls gucken wir ob der rechte Nachfolger noch existiert,
dann nennen wir den X und sonst nennen wir X Neil.
Sonst wird X Neil. Also wir wissen, dass nur eins
ein Kind existieren kann zu diesem Zeitpunkt. Und jetzt müssen wir,
können wir den Knoten löschen und X zum neuen Kind von dem Vorgänger von P machen,
indem wir einfach sagen,
der Parent von X wird der Parent von P von Z.
Dann wird der also unter dem Vorgänger gehängt. Und wenn das null war,
wenn der Parent von Z null war,
dann hatten wir die Wurzel, die wir jetzt gerade gelöscht haben,
dann müssen wir den Root-Zeigern neu setzen.
Und was wir sonst ansonsten noch machen müssen, ist,
dass wir den Left und Right Pointer des Vorgängers entsprechend auf das X setzen.
Und damit haben wir den Knoten auch entfernt. Also das
sind die beiden Funktionen, die wir haben auf dem Treep,
um Knoten nach unten oder nach oben zu verschicken.
Okay, das zu den Treeps. Ich werde hier die Analyse
von den Treeps nicht machen. Da ist auch so viel eigentlich nicht dann zu tun,
was man, was interessant ist, ist,
dass man sich anschaut, was passiert eigentlich bei zufälligen Verteilungen von Prioritäten,
weil dann kann man nämlich zeigen,
dass man auch hier wieder lugorithmische Zeit hat
Aber ansonsten ist die Analyse, glaube ich, klar.
Die Laufzeiten hängen ab von der Tiefe und die Tiefe kann man aber nicht balancieren.
Die hängt von den Prioritäten ab. Und im Worst Case
sind die Tiefen eben nicht mehr logorithmisch, sondern nur noch in N.
Okay, also zu den Triebs kann man sagen,
sie vereinigen suchbar und Hiebeigenschaft in einer Baumstruktur und jeder Knoten hat dann Schlüssel und eine Priorität.
Die Rotation nutzen wir jetzt zu einem anderen Zweck und zwar
dazu Dass wir die Hieb-Eigenschaften nacheinfügen oder löschen, reparieren.
Also das ersetzt quasi das Hieb-Pify. Wir können nicht balancieren,
weil wir quasi eine Art,
weil der Treep quasi eindeutig ist für einen Datensatz,
vorausgesetzt, alle Schlüssel sind unterschiedlich und alle Prioritäten sind unterschiedlich.
Wann nützen uns die Treeps was? Die nützen uns dann was,
wenn wir Wörterbuchprobleme haben, also Suchanfragen, wo wir die Zugriffswahrscheinlichkeiten kennen.
Und diese Zugriffswahrscheinlichkeiten können wir nutzen als Prioritäten und damit steuern,
dass Knoten mit einer hohen Zugriffswahrscheinlichkeit weit oben im Baum sind.
Das ist quasi Das Ziel. Und zudem haben wir sogar
die Möglichkeit, online, während der Algorithmus läuft,
diese Zugriffswahrscheinlichkeiten zu ändern und die Baumstruktur anzupassen.
Weil wir gesehen haben, dass wir analog zu der Hipify-Operation
quasi Prioritäten anpassen können und den Baum oben organisieren können.
So, wenn wir jetzt mal,
wenn wir Suchbäume haben und wir würden mal zufällige Prioritäten verwenden.
Wir würfeln die aus, die Prioritäten. Dann können wir sicherstellen,
dass so ein Tree entsprechend ein randomisierter Suchbaum ist.
Ja, das ist also ein Suchbaum, der erfüllt quasi
die Baumeigenschaft, die Suchbaumeigenschaft. Und wir geben aber quasi,
wir wählen den Baum aus, durch die Prioritäten.
Wir entscheiden, welchen Baum wir nehmen. Das sagen die
Prioritäten und die Prioritäten bestimmen wir über einen Zufallzahlengenerator.
Damit kriegen wir einen zufälligen Suchbaum, der unabhängig ist von
der Verteilung der Eingabedaten und auch von der Reihenfolge,
in der wir Objekte in die Datenstruktur einfügen.
Wenn Sie sich noch so ein bisschen erinnern an die randomisierte Analyse,
dann war das immer, war es für uns wichtig,
dass wir quasi das Algorithmische von dem als Prozess so ein bisschen trennen,
damit wir nicht anhängig von der Eingabe ein spezielles Verhalten haben.
Das kann man hiermit machen. Man kann also die Suchbäume,
die Treeps nutzen, um bei Suchbäumen eingabeunabhängig sicherzustellen,
dass wir einen zufälligen Suchbaum haben.
Also zufällig nur bezüglich, nicht bezüglich der Key-Eigenschaft,
das ist natürlich ein Suchbaueneigenschaft, aber bezüglich der Verteilung der Balance.
Okay, wenn dem so ist, dann können wir zeigen,
dass so ein Treep im mittleren Fall luggerithmische Laufzeit hat.
Die Worst-Case-Zeit ist zwar O von N, im ungünstigsten Fall
haben wir eine schlechte Verteilung und in unbalancierten Baum.
Aber ansonsten haben wir im mittleren Fall Login und das Einfügen und Löschen
hängt ja auch nur von der Tiefe des Baumes ab.
Und das würde dann ebenfalls in Login-Zeit funktionieren. Okay,
das ist sozusagen das Ende dieses Kapitels. Und hier nochmal zusammengefasst,
Sie haben ein bisschen uns mit Suchbäumen und mit Heeps beschäftigt.
Bei den Suchbäumen haben wir am Anfang die Splaytries gesehen.
Die waren selbstorganisierend und haben die Höhe der Knoten adjustiert
Dann konnte man in einer rechtskomplexen Analyse zeigen,
dass amortisiert die gleiche Performance erreicht wird,
wie bei balancierten Suchbäumen, ohne dass wir eine Zusatzinformation,
wie zum Beispiel eine Färbung, wie beim Rot-Schwarzbaum angeben müssen.
Und wir haben die Treeps gesehen. Das sind quasi,
ja, V-Splaytrees, lassen Sie sich verwenden,
um eine Organisation durchzuführen in den Suchbäumen,
also die nicht durch Balance kontrolliert ist, sondern durch Prioritäten,
durch Zugriffsprioritäten im Unterschied zu den Splaytrees,
die sozusagen immer davon ausgehen, das Letzte,
was zugegriffen wurde, soll möglichst an der Wurzel stehen.
Ist es hier so, dass die Prioritäten explizit vorgeben können und damit steuern,
auf welcher Höhe im Suchbaum der Knoten steht.
Und da kann man immerhin zeigen, dass im mittleren Fall
die gleiche Performance da ist, wie bei balancierten Suchbäumen.
Dann haben wir Heeps gesehen, die binomialen Heeps und die Fibunachi Heeps,
das sind einfach andere Möglichkeiten, Prioritätswarteschlangen zu realisieren,
die eben auf einer Menge von binomialen Bäumen basieren.
Die binomialen Bäume sind etwas, was man einfach mal gesehen haben sollte,
weil es Baumstrukturen sind, die zwar nicht mehr einen konstanten Grad haben,
die wo der Grad aber luggerithmisch beschränkt ist und die Höhe luggerithmisch beschränkt ist.
Ja, eine nicht ganz unwesentliche Eigenschaft,
wenn sie ja solchen Baum durchsuchen, von der Wurzel zu einer,
nee, von dem Platz zu einer, zur Wurzel,
geht es in luggerithmischer Zeit, in umgekehrter Richtung,
in Log-in-Quadratzeit, weil sie immer erst alle Kinder und gucken müssen,
um zu entscheiden,
in welche Richtung sie gehen.
Die Venomialen Heeps basierten dann auf den binomealen Bäumen und ermöglichen damit
eine relativ effiziente Union-Operation. Das war erstmal der wesentliche Vorteil,
die Fibunaci Heeps waren dann die Weiterentwicklung. Und das war eigentlich der Hauptgrund,
warum wir uns die Venomialen Heeps angeguckt haben,
weil die eben mit einer amortisierten Analyse,
die die Kreas Key Operation hier in konstanter Zeit realisieren.
Und damit die asymptotische Laufzeitschranke des Dicstraw und des Prim-Algorithmus
auf O von V-Log V plus E singt.
Okay, so, das war das Datenstruktur-Kapitel.
Und ja, wir haben noch eine halbe Stunde Zeit.
Ich zeige Ihnen mal schon mal über grob, wie es weitergeht.
Wir werden da jetzt heute nicht mehr allzu tief einsteigen,
aber ich kann Ihnen schon mal ein bisschen erzählen,
was wir dann im Januar machen.
Also wir haben jetzt noch zwei Kapitel vor uns.
Im nächsten Kapitel werden wir uns numerische Probleme anschauen.
Und das sind sozusagen die Kapitel, die es da gibt.
Wir werden auch wieder ein Wiederholungskapitel da drin haben,
was sich dann ein bisschen schneller machen werde
Und zwar ist es das zweite Kapitel über Matrix-Kettenmultiplikation.
Das lernt man normalerweise in Algorithmen und Datenstrukturen schon kennen,
weil es einfach das Paradebeispiel für dynamische Programmierung ist.
Deswegen mache ich das relativ flott.
Wir werden anfangen mit Matrix-Multiplikationen und dieses Multiplikationsverfahren genau anschauen,
weil das eben die zentrale Operation ist für viele numerische Verfahren,
wo man dann bei großen Matrizen schnelle Algorithmen benötigt,
werden dann uns Gleichungssysteme angucken.
Das sind sozusagen die Standardverfahren, die es so gibt,
um Gleichungssysteme zu lösen. Wir werden hier ein bisschen genauer schauen,
wie man die realisiert und werden dann zu dem ganz wichtigen Kapitel in diesem Teil hier reinkommen,
nämlich in die Vinneare Programmierung und von dort dann in die ganzteilige Programmierung.
Das ist für, diese beiden Kapitel sind wichtig für so mathematisch formulierte Optimierungsprobleme.
Da werden wir den Simplex-Algorithmus kennenlernen. Dann haben wir noch
ein fünftes Kapitel, was wir im Januar machen werden.
Das ist die, wird sich mit algorithmischer Geometrie beschäftigen,
also mit der Lösung geometrischer Probleme.
Ja, vielleicht
schon mal als Einstieg
Wir werden, warum gucken wir uns das an?
Ja, warum gucken wir uns jetzt diese numerischen Fragestellungen an?
Ja, weil die eben auch häufig auftreten.
Also wir haben bisher viel uns mit diskreten Problemen auseinandergesetzt,
die ganze Grafalgorithmik ist diskret, mehr oder weniger. Und wir haben halt viele Prozesse,
zum Beispiel im wissenschaftlichen Rechnen, wenn wir Simulationen machen,
wenn physikalische Abläufe beschrieben werden, Approximationsverfahren, all diese Verfahren benutzen,
ja, eine numerische Systeme, um das Ganze, den ganzen Prozess zu beschreiben. Ja,
und nicht nur da, ja,
wenn sie Wenn Sie ein modernes User-Interface ansehen, das,
was eines der Komponenten,
die Apple ja so perfektioniert hat und so so weit in dem User-Interface-Design nach vorne gebracht hat,
ist, dass physikalische Verhalten von User-Interface-Elementen, ich weiß nicht,
ob Ihnen das schon mal so aufgefallen sind, das ist das,
was zumindest vor einigen Jahren der große Unterschied ausgemacht hat,
dass sich das mal das Gefühl hat,
wenn man irgendwo drüber gestrichen ist,
als wenn es tatsächlich so wäre,
als wenn man
etwas Reales bewegt hat
Etwas mit, genau, auch solchen Prozessen zu tun,
dass man solche Bewegungsabläufe quasi exakt beschreibt. Heute ist es so,
dass andere Betriebssysteme sowas auch können, ja,
und das ist genau der Grund, warum alle Geräte,
die mit irgendwelchen Touch-Displays arbeiten, heute so beliebt sind,
ja, weil es einfach so viel Spaß macht,
Blätter so auf dem Computerbildschirm hin und her zu schieben.
Maschinelle Lernverfahren haben wir, ich weiß nicht,
wer von Ihnen maschinelles Lernen als Wahlpflichtfach hat,
da arbeiten Sie natürlich mit großen Matrizen,
berechnenden Korrelationen zwischen Eigenschaften und Daten etc., das sind Sachen,
wo wir viel mehr mit Matrizen und numerischen Werten arbeiten.
Und dann haben wir natürlich die ganze Optimierung.
Also viele Prozesse, die wir beschreiben müssen, sind Optimierungsprobleme.
Zum Beispiel in der Wirtschaft, wenn wir Logistikprobleme lösen wollen, etc.
Und meistens hat man es hier mit Gleichungssystemen zu tun,
die Randbedingungen definieren. Und dann mit einer Zielfunktion,
die gelöst werden muss, die die Optimalität einer Lösung beschreibt.
Ja, die Grundlage von dem Ganzen sind Matrizen und Vektoren.
Ich habe so ein paar Folien hier, die das beschreiben.
Ich werde natürlich hier in der Vorlesung keine Einführung in
lineare Algebra machen
Dafür haben wir die Zeit nicht und viele von ihnen werden das wahrscheinlich auch gut kennen.
Es ist auch so, dass wir für die Algorithmen nicht
so viel davon benutzen. Also für den Fall,
dass sie das in ihrem Studium bisher nur so tangiert haben,
ist es auf alle Fälle sinnvoll, mal sich mit diesen Begriffen auseinanderzusetzen.
Deswegen wiederhole ich die hier. Ansonsten ist es aber auch
nicht so, dass wir sehr tief dort einsteigen.
Aber wenn ihr die Begriffe auf den nächsten sechs,
sieben Folien so gar nichts sagen, dann,
und sie haben noch ein bisschen Zeit zwischen Weihnachten und Neujahr,
dann wäre es vielleicht eine gute Idee,
sich das nochmal anzuschauen
Okay, was wir haben werden, sind Matrizen,
zweidimensionale Zahlen, Zahlenfelder mit Heilen und Spalten.
Und es gibt natürlich eine Reihe von speziellen Matrizen,
eine Null-Matrix, wo alle Werte null sind,
eine quadratische Matrix, wo die Anzahl der Zahlen spalten.
Gleich ist eine Diagonalmatrix, das ist eine,
wo nur Werte auf der Diagonalen stehen.
Die Diagonale geht immer von oben links nach unten rechts.
Und alle anderen Werte null sind und eine Einheitsmatrix,
wo auf dieser diagonalen Einsel stehen und alle anderen Werte null sind.
Vektoren sind nichts weiter als Matrizen mit einer Spalte.
Die sehen dann entsprechend so aus und es gibt natürlich
auch Einheitsvektoren,
ein Einheitsvektor
Der IT-Einheitsvektor hat die Eigenschaft, dass das IT-Element
des Vectors 1 ist und alle anderen Elemente null ist.
Das ist also die IT-Spalte aus einer Einheitsmatrix. Solche Matrizen können so aussehen,
dass in einem Dreieck davon oberhalb der Diagonale oder auch unterhalb der Diagonale
nur Nullen drin stehen, dann reden wir von einer Dreiecksmatrix.
Das ist also hier die untere Dreiecksmatrix,
weil unten Werte drin stehen und oben nur Nullen drin stehen.
Entsprechend gibt es eine obere Dreiecksmatrix, die entsprechend andersherum aussieht.
Beachten Sie das auf der Diagonalen bei der Dreiecksmatrix
durchaus noch Werte
stehen können
Diese Matrizen, die haben wir auch relativ häufig in
der Praxis in Permutationsmatrizen, Permutationsmatrizen, haben die Eigenschaft,
dass in jeder Zeile und in jeder Spalte nur eine Eins vorkommt und alle anderen Werte null sind.
Permutationsmatrizen haben die Eigenschaft, dass wenn man sie multipliziert mit
einer anderen Matrix, davor und dahinter transponiert,
dann kann man damit die Zeilen und Spalten einer Matrix vertauschen.
Eine Matrix beseitnen wir als symmetrisch, wenn sie die Eigenschaft hat,
dass sie so eine Spiegelsymmetrie an der Hauptdiagonalen haben,
also wenn ein Wert AIJ dem Wert AJI entspricht.
So wie hier, sie können halt immer gucken,
dann immer gleich und dann ist die entsprechend symmetrisch.
Was für Operationen machen wir auf den Matrizen? Wir haben
Transpositionen, das ist die Vertausschung von Zeilen und Spalten.
Das ist quasi so ein Spiegeln um der Hauptdiagonale.
Das geht von dieser Matrix in die Transponierte über.
Wir haben eine skalare Multiplikation, wo wir einen Lander mit der Matrix multiplizieren,
indem wir alle Werte mit einem Lamda multiplizieren.
Damit können wir natürlich einem Matrix auch negieren,
indem wir alle Werte mit minus eins multiplizieren.
Wir können sie addieren, indem wir alle Elemente addieren,
wir können sie subtrahieren,
indem wir die negierte Matrix addieren und wir haben
die Matrix Multiplikationen
Matrix Multiplikation ist etwas, was für jemanden,
der nicht mit Matrizen gewohnt ist, zu arbeiten,
ungewöhnlich ist, weil wir eben nicht Elemente für Elemente miteinander multiplizieren,
sondern weil wir ein Zeilenspaltenprodukt berechnen,
das heißt, um ein Element zu berechnen,
nehmen wir eine Zeile und multiplizieren sie elementweise mit der Spalte des Nächsten,
der nächsten Matrix und addieren die Werte und kommen dann entsprechend auf das Resultat.
Ja, das heißt, ein Element CIK in der
resultierenden Matrix ist die Summe von J gleich 1 bis NAIJBJK
Ja, das ist die Matrix-Multiplikation. Die Rechenregeln,
die dann herauskommen ist,
wir haben eine Addition mit einem neutralen Element Null,
das ist die Null-Matrix, A plus Null wäre A,
0 plus A, RRA, wir haben die Kommunitativität,
A plus Bs, B plus A,
das überträgt sich einfach von der normalen Addition minus A,
ist das inverse Element, A plus minus Eis,
die Null-Matrix.
Wir haben die Multiplikation, A mal Null,
ist gleich Null mal A, gleich Null,
genauso wie wir das auch bei Zahlen haben.
Wir haben die Einheitsmatrix als neutrales Element,
also wenn A eine Matrix mit M fielen, Zeil,
M fielen Spalten, ist, nee,
in vielen Zeilen ist, dann ist
IM das neutrale Element. Wenn wir das also damit multiplizieren,
kriegen wir über die Matrix A raus.
Hier steht ein M und hier ein N für den Fall,
das A, keine quadratische Matrix ist.
Es gilt auch das Assoziative und das Distributivgesetz,
genauso wie wir das von Zahlen kennen. Das heißt,
A mal C mal D ist das Gleiche wie A mal C, Made.
Oder A plus B mal C ist das Gleiche wie A mal C
plus B mal C. Was nicht gilt, ist die Komutativität.
Das heißt, es gilt im Allgemeinen nicht das A mal C
gleich zehnmal A ist. Das ist die eine Eigenschaft,
die für Matrix Multiplik als nicht im Allgemeinen gültig ist.
Und das muss man beachten. Ich bin mir nicht ganz sicher,
aber es gibt es nicht auch im Allgemeinen kein neutrales Element
immer für Nicht-, quadratischer Matrizina, für quadratische Jahre.
Es gibt immer ein neutrales Element, was Sie meinen,
ist ein inverses Element. Das gibt es nicht immer.
Was wir, was wir gewohnt sind von einer Multiplikation,
ist, dass es ein inverses Element gibt.
Und wenn wir mit dem inversen Element multiplizieren, kommt der Einheitswert raus.
Also bei normalen Zahlen ist es der Kehrwert.
Also für das Inverse-Element von 5 ist ein Fünftel,
weil 5 mal ein Fünftel wieder 1 ist
So ein inverses Element hätten wir auch gerne für Matrizen.
Und auch hier ist die Situation jetzt etwas schwieriger als bei Zahlen,
so eine Inverse, wenn sie denn existiert,
bezeichnen wir mit A auch minus eins.
Und die Eigenschaft, die diese Universe haben soll, ist,
dass A mal A auf minus eins die Einheitsmatrix ist,
beziehungsweise auch minus eins mal A, sollte auch die Einheitsmatrix sein.
Das bezeichnen wir als Inverse. Inverse können nur existieren für
quadratische Matratzen. Also wenn sie nicht quadratisch sind,
gibt es sowieso keine Inversen.
Eine Matrix, die eine Inverse hat,
die nennen wir Inventierbar
Und wenn sie keine hat, ist sie eben nicht invertierbar
oder auch singulär. Ja, wenn sie invertierbar ist,
nennen wir sie auch regulär. Wenn so eine Inverse existiert,
dann gilt das B mal A hoch minus 1.
Das Gleiche ist, wie A hoch minus 1 mal B
hoch minus 1. Und das ist an dieser Stelle wichtig,
weil im Allgemeinen ja die Kommunikativität der Multiplikation nicht gilt.
Transponieren und invertieren können wir austauschen. Das heißt auch minus 1
hoch T, also transponiert, ist das Gleiche wie A
transponiert,
hoch minus
Die Vektoren,
also wenn wir jetzt die einzelnen Spalten einer Matrix betrachten,
die bezeichnen wir als linear abhängig, genau dann,
wenn es einen Vektor gibt mit N-Elementen, die sind alle reellwertig,
von denen mindestens einer ungleich null sein muss,
sodass das Produkt über die Vektoren multipliziert mit diesen CEs gerade null ergibt.
Ja, dann sind die linear abhängig und sonst sind die linear unabhängig.
Dementsprechend haben Matrizen einen Spalten und einen Zeilenrang,
der Spaltenrang ist die maximale Anzahl Linie unabhängiger Spalten,
der Zeilenrang,
die maximale Anzahl unabhängiger
Zeilen. Und man kann zeigen, dass der Zeilenrang mit der Schwalbenrang
identisch ist und das wird der Rang einer Matrix genannt.
Dann gibt es den Null-Vektor und jetzt muss man aufpassen,
dass man den nicht verwechselt, die Null-Matrix-Ware, die,
die überall nur Null stehen, hat ein Null-Vektor, ist ein Vektor,
der eben nicht komplett Null ist, der aber die Eigenschaft hat,
dass wenn man diesen Vektor mit einer Matrix multipliziert, der Wert Null rauskommt.
Und weil man sich das vielleicht nicht so gut vorstellen kann,
hier ein Beispiel, hier ist eine Matrix,
hier ist ein Vektor und wenn sie A mal X ausrechnen,
kommt da Null raus, das liegt daran, weil diese Spalten hier,
die sind linear abhängig und wenn sie dies, wenn sie es entsprechen,
das sind die koeffizienten C und wenn sie das,
mal das immer multiplizierend aufbadieren,
kommt immer null raus.
Also, dann gibt es in der,
in den Grundlagen der linearen Algebra eben diese wichtigen Theorieme,
die den Rang mit der Invertierbarkeit verknüpfen.
Und zwar kann man zeigen, dass der Rang von A,
wenn der Rang von AN ist,
also wir reden nur noch von quadratischen Matrizen, dann ist A eine reguläre Matrix.
Wir können sie also invertieren. Und wir können auch sagen,
wenn wir eine M-Gruppe als N-Matrix haben,
wenn der Rang von AM ist, wenn der M ist,
dann wissen wir, dass A keinen Null-Vektor besitzt.
Eine N-Kreuz-N-Matrix, die ist singulär, genau dann,
wenn A, eine Null-Vektor besitzt.
Also diese Existenz von Null-Vektor zeigt die lineare Abhängigkeit, dadurch sinkt der Rang,
der wird dann nicht mehr eng sein und dann kann man diese Atritzen nicht mehr invertieren.
Dieses Beispiel, Matrix hier, die hatten Rang N-minus 1,
ist singulär und der Null-Vektor, der steht hier,
wenn man das damit multipliziert,
kommt eben Null raus und deswegen ist die auch nicht invertierbar
Okay, das brauchen wir. Später, wenn wir Gleichungssysteme lösen.
So, das erste Problem, was wir uns anschauen werden,
ist, dass der Matrix-Multiplikation. Und das ist ein Problem,
was auf den ersten Blick eigentlich nicht der Betrachtung wert scheint.
Ja, also hier ist quasi als Pseudocode aufgeschrieben,
wie eine Matrix-Multiplikation funktioniert. Und zwar muss die Anzahl der
Spalten von A gleich der Anzahl der Zeilen sein.
Das können wir das
nicht machen
Und ansonsten müssen wir eine Fehlerheldung ausgeben. Das ist so
eine inkompatible Dimension. Die Matrizen können nicht multipliziert werden.
Anderenfalls haben wir jetzt also zwei geschachtelte Vorschleifen. Das ist nicht
so schön eingerückt hier. Die geht einmal mit I über
die Anzahl der Zeilen und mit J über die Anzahl der
Spalten der Matrix B. Wir berechnen dann das neue Element CJ,
der Ergebnismatrix,
in dem wir mit einem Index K von 1 über die Anzahl
der Spalten von A laufen und das C-E-J berechnen als C-E-J plus A-I-K mal BKJ.
Das ist also nichts weiter als die Umformung oder
die Umsetzung von der Definition der Multiplikation
als ein Pseudocode
So, diese Multiplikationsoperation wird also viel benutzt und auf großen Matrizen verwendet.
Ja, also wir müssen große Matrizen multiplizieren in der Praxis.
Und wir können sofort sehen, wie aufwendig dieser Algorithmus ist.
Wir haben nämlich eins, zwei,
drei geschachtelte Vorschleifen und wenn wir mal davon ausgehen,
dass die Anzahl der Zeilen und Spalten in beiden Madrizen gleich ist und durch N beschränkt ist,
dann hätte dieser Algorithmus also O von N hoch drei Laufzeit.
Der würde dann von hier von 1 bis N gehen,
noch eine Schleife von 1 bis N,
noch eine Schleife von 1 bis N. O von N
Ja, das ist für mich ein Beispiel für einen Algorithmus,
wo ich mit ziemlicher Sicherheit,
wenn ich ihn das erste Mal sehen würde,
behaupten würde, dass dieser Algorithmus laufzeitmäßig nicht optimierbar ist.
Ich weiß nicht, ob sie der anderer Meinung sind,
also ob sie das Gefühl haben,
dass das besser geht als in Nhoch 3. Ja,
was soll man da noch tun?
Ja, also ich meine, man läuft über die Zeilen
und Spalten und multipliziert das und hat ein Verfahren,
das braucht einfach jetzt Endhof 3, weil man will Werte multiplizieren muss.
Auf alle Fälle haben wir eine untere Schranke von N Quadrat.
Warum haben wir die? Naja, weil die Ergebnismatrix C,
die wir ja geben müssen. Wie hat ja Enquadrat viele
Einträge und wir müssen alle Einträge berechnen und ausgeben und schreiben.
Und deswegen haben wir Enquadrat viel Zeit,
um die wir mindestens brauchen, um das Ergebnis zu produzieren.
So, jetzt tut sich hier eine Lücke auf, ja.
Also wir haben eine untere Schranke von N-Quadrat und wir haben eine obere Schranke von,
oder wir haben einen Algorithmus mit N-Hoch 3 Laufzeit.
Ich glaube, dass das Verfahren quasi von einem ganz großen Teil
der Fachwelt lange, lange Zeit für absolut nicht optimierbar gilt.
Das war eben mein Enhoch 3 Verfahren, ja,
um Matrizen zu multiplizieren
Man kann es aber schneller machen. Ja, man kann
tatsächlich Matrizen mit einer besseren oder einer geringeren Laufzeit multiplizieren.
Und zwar, das hat, wer von Ihnen kennt das?
Kennt das einer von Ihnen schon? Sie kennen das, okay.
Einer. Und zwar ist das ein Ergebnis von Herrn Straßen
. Der war der Erste, der es gezeigt hat.
Und der hat es geschafft, ein Verfahren anzugeben,
was die Laufzeit reduziert hat von O von Nhoch 3
auf O von N hoch
Ja. Selbst wenn es 2,99 gewesen wäre,
wäre es total faszinierend gewesen, dass das geht.
Ja. Und Herr Straßen war derjenige, der sozusagen den
Auslöser gegeben hat und gesagt hat, das geht tatsächlich schneller.
Und heute ist das nicht mehr das beste Verfahren.
Heute hat man dann alle möglichen Geschichten gemacht, um das zu optimieren.
Ich weiß, die aktuelle Konstante nicht. Also das ist
irgendwo bei N hoch, müssen wir mal nachgucken, 2,3, irgendwie so.
Da ist momentan der aktuelle Wert für die Zeit,
die man braucht, Pritzen zu multiplizieren.
Und jetzt sehen Sie schon, wir haben hier unsere untere Schranke von N-Vordrat.
Und man nähert sich tatsächlich an. Das ist irgendwie faszinierend,
dass das tatsächlich geht. Und dieses Verfahren von Straßen ist tatsächlich so,
das hat zwar auch große Konstanten, was wir schon häufig gesehen haben,
wenn wir im Ulkalkül was verbessern, dann steigen die Konstanten.
Aber wenn man große Matrizen hat, lohnt sich das.
Das ist ein gravierender Unterschied in der Laufzeit zwischen den beiden Verfahren. Ja,
den Algorithmus will ich heute jetzt nicht mehr anfangen,
aber ich möchte Ihnen mal ein Bild zeigen,
Immer wenn ich an diesen Straßenalgorithmus denke, dann fällt mir das ja ein, kennt das?
Kennt jemand von Ihnen? Sam Lloyd? Also Sam Lloyd
war jemand, der hat hier in den USA gelebt,
Ende des, was muss man da jetzt sagen?
Vor, vorherigen Jahrhunderts? Ist das richtig? Naja,
Sie wissen, was ich meine. Da hat,
vor, vor letzten Jahrhunderts, also da hat er gelebt
und das war jemand, der hat irgendwie so Puzzle entworfen.
Der hatte so Puzzle irgendwie gemalt. Und das berühmteste Puzzle von ihm
ist das folgende, was dann irgendwie hier in der Zeitung abgedruckt wurde, 18196.
Und das hieß Get of the Earth. Ja,
und wenn sie sich das angucken, dieses Puddel war so gemacht,
dass hier sehen sie so Piraten.
Und diese Weltkugel hier, die war ausgeschnitten.
Also hier genau entlang der Kugel war das ausgeschnitten.
Und die konnte man drehen, um diesen, um den Punkt.
Niemand will man so drehen. Und wenn sie jetzt hier gucken,
dann sehen sie hier ein, zwei, drei, vier,
fünf, sechs, sieben, acht, 19, 11, 12 Piraten.
Ja? Und wenn man das jetzt,
um irgendwie so ein paar Grad nach rechts dreht, diese Kugeln,
die sehen, die ist jetzt so nach rechts gedreht,
dann sehen sie ein, zwei, drei, vier,
fünf, sechs, sieben, acht, neun,
zehn, elf, zwölf, 13 Piraten.
Ja. Es kommt nicht schon mal aus so einem Film,
The 12 Monkeys gehört. Ja,
also vielleicht kennen sie auch Jim Knopf und die wilde 13.
Müssen Sie, wie viele Piraten da sind?
Bei der wilden 13? Können Sie mal nachgucken, ja.
Also, hier verschwindet einer durch das Drehen von dieser Scheibe.
Und die Frage ist natürlich, wo bleibt der?
Und es hat die Leute damals total fasziniert. Ich finde das
bis heute total faszinierend und wenn sie das jemandem zeigen,
der das noch nie gesehen hat, ist er erstmal auch total irritiert.
Wenn man dann ein bisschen darüber nachdenkt, dann findet man natürlich raus, was passiert.
Natürlich kann da ja nichts verschwinden und der Trick,
den der Sam Lloyd hier angewendet hat, ist,
dass er quasi so ein klein bisschen Pirat auf alle anderen verteilt.
Also da, wo sie, wo sie zwölf Piraten haben,
sind alle Piraten quasi ein bisschen größer, als da,
wo sie 13 Piraten haben und die Art und Weise,
wie er das macht, ist,
dass der Schnitt sozusagen sich verlagert.
Also hier schneidet er irgendwie so ein bisschen nur vom Bein
ab und dann wird das sozusagen immer mehr und dann verschiebt
sich das und entsprechend verschwindet dann ein Pirat.
Ist super toll gemacht, ja,
und es gibt auch noch ein anderes Beispiel hier,
da ist das ein bisschen leichter, da kann man das sehen.
Das ist hier The Magic Ac. Hier sehen wir so ein Bild,
da haben wir ein, zwei, drei, vier,
fünf, sechs, sieben, acht, neun Eier.
Das war genauso gemacht, da war hier,
hier war das durchgeschnitten, ja, und hier war das durchgeschnitten.
An diesen beiden Stellen waren das zwei Teile. Das obere war ein Teil,
wo der Hahn hier ist, ne,
und diese unteren beiden Teile, die konnte man vertauschen,
dass man einfach diese Schablone oder dieses Teil nach links gelegt hat man nach rechts gelegt.
Und dann sah das Ganze so aus. Und jetzt hat
man nämlich nur noch ein, zwei, drei,
vier, fünf, sechs, sieben, acht Eier.
Und eins ist weg. Und der Trick ist genau der gleiche.
Und hier kann man es natürlich auch ein bisschen besser noch nachvollziehen,
was eigentlich passiert ist, dass quasi diese Eier hier alle
ein ganz klein bisschen kleiner sind als diese Eier.
Und quasi ein Ei sich aufteilt auf die anderen.
Es ist eine optische Täuschung. Es ist einfach
nur ganz toll gezeichnet. Ja, und warum,
was hat das das Ganze jetzt mit der Matrix-Multiplikation
zu tun
Das, was ja strassen macht, ist,
dass er aus einer Menge von Teilmultiplikationen, die er hat,
also er rechnet sozusagen entsprechende Teile aus, einen Teil verschwinden lässt.
Er lässt ein Teil verschwinden,
indem er ihn ersetzt durch andere Multiplikationen,
durch andere Berechnungen, die er vorher schon gemacht hat.
Und dadurch reduziert er die Laufzeit. Das wird der Trick sein.
Okay, den werden wir heute aber nicht mehr machen.
Ich habe aber noch ein schönes Bildchen für Sie
zum Abschluss.
Und zwar das hier
Dass ich ihnen noch zeigen muss, um zu vermeiden,
dass sie demnächst ausgeschlossen werden, weil sie die ganze Zeit in den letzten Monaten
nur über Algorithmik nachgedacht haben und sich anlegen mit den Leuten,
die es nicht getan haben.
Und mit diesem schönen Bild wünsche ich Ihnen erstmal alles Gute für die
nächsten paar Tage und wir sehen uns hoffentlich alle wieder Anfang Januar.
Okay,
das war's