Alles klar. So, willkommen bei Algorithmen und Datenstrukturen.
Kommt mir heute so laut vor. Jetzt bin ich laut für Sie
oder ist es okay? Nö. Okay, gut. So,
wir sind bei Hash-Verfahren und wir haben in der letzten Vorlesung
zwei Möglichkeiten gesehen, wie wir Kollisionen vermeiden können.
Das erste war Hashen mit Faketten. Da haben wir uns
den größeren Teil der Vorlesung mit beschäftigt und Da werden Kollisionen,
die beim Hashing auftreten, vermieden, indem man Listen verwendet.
Und dann hat man für jeden möglichen Hash-Index eine Liste.
Und wenn zwei Elemente auf den gleichen Index gehasht werden mit
Hash-Funktionen, wird einfach die Liste verlängert.
Das war die erste Strategie. Und da haben wir Gründe gesehen,
warum das Nachteile hat, also insbesondere den großen Nachteil,
dass man nicht von vornherein einfach einen Speicherbereich reservieren kann und sagt,
das ist jetzt für die Speicherung der Daten,
sondern es wurde immer dynamisch, noch zusätzlich Speicher angegefordert für die Listen.
Und die Alternative dazu war die offene Adressierung,
Und bei der offenen Adressierung macht man eigentlich auch so was wie Listen.
Allerdings baut man nicht Explizitlisten, sondern man verwendet eine Funktion,
die einem von einem Element aus sagt,
was die nächste Position ist, wo wir das Datenelement speichern könnten.
Und dann können wir die Liste quasi hineinprojizieren in ein
festes Array und das dann entsprechend verwenden.
Das ist das, was wir bei offener Adressierung machen.
Wir haben natürlich nicht wirklich Listen, das ist ganz wichtig,
so was im Kopf war.
Das sind Sequenzen von Hash-Adressen, die wir nacheinander in einer
festen Reihenfolge durchprobieren
Das bedeutet unter anderem, dass wir auf, wenn wir diesen Sequenzen folgen,
wir durchaus Werte finden können, die bezüglich der Hash-Adressen nicht zusammengehören.
Also bei der Liste gehören die immer, haben die alle eine Kollision verursacht.
Hier ist es tatsächlich so, dass es sein kann,
dass ein Datenelement ursprünglich einen anderen Hashky hatte,
aber quasi entlang dieser Verkettung auftaucht,
weil wir einfach dann bei dem Eaten-Sondierungsversuch auf einfach den gleichen Index treffen.
So, das war so ein Beispiel,
an dem man sehen konnte, wie das funktioniert.
Also nur nochmal ganz kurz, also wenn wir,
wir haben haben die Hash-Funktionen, die einfach K-Moduluseben nimmt.
Und wir brauchen in der Hash-Funktion immer einen zweiten Parameter.
Das ist der Sondierungsversuch. Und das bedeutet hier, wenn der IWERT null ist,
dann nehmen wir einfach Modulusiven, verwenden das als Index,
sodass wir die 15 zum Beispiel an der Position 1 speichern würden.
Wenn diese Stelle belegt ist und wir wollen eine Zahl dort speichern,
dann zählen wir hier den Sondierungsversuch hoch,
das I und das nächste,
was wir dann probieren würden,
wäre dann die Position,
daneben die Position 2 und dann würden wir an die 36
speichern und dann haben wir uns eine ganze Folge von Aktionen angeschaut,
wie das Ganze dann aussehen
Kann. Ich muss mal diesen, so kann das natürlich nicht funktionieren. Okay, so,
dann haben wir uns noch mal kurz mit den Hash-Funktionen auseinandergesetzt,
überlegt, was wir gerne nicht, was wir gerne vermeiden wollen,
also das war das primäre Clustern, das Entstehen von langen Ketten,
deswegen wollen wir nicht einfach immer von einem Element zum nächsten gehen,
das ist ungünstig und auch das sekundäre Clustern ist nicht so schön,
was bedeutet, dass zwei Werte,
die eine Kollision haben, quasi die gleiche Sondierungssequenz haben,
das ist auch nicht so günstig und die Strategie,
mit der man beides sehr gut vermeiden kann,
ist das sogenannte doppelte Das ist also die Technik,
die man anwenden sollte.
Man verwendet zwei Hash-Funktionen, H1 und H2,
die idealerweise unabhängig voneinander sind. Das heißt, die Wahrscheinlichkeit,
dass sie gleichzeitig für einen Schlüssel eine Kollision produzieren, ist sehr gering.
Ja, das ist wichtig, weil wenn das dann so ist,
dann können wir als Hash-Funktion einfach sagen, okay,
wir nehmen den ersten Hash-Schlüssel und dann verwenden wir den zweiten als quasi Sprunglänge.
Und mit jedem Sondierungsversuch springen wir dann um H2 von K
weiter in der Folge. Und der große Vorteil davon ist,
dass wir jetzt, wenn wir einen Schlüssel haben,
wo wir tatsächlich eine Kollision hätten,
dann würden wir bei der ersten Sondierung die Kollision haben,
aber schon bei dem zweiten Sondierungsversuch würden wir ja dann die Hash-Funktion H2 verwenden und da drauf addieren.
Da würden die beiden Elemente mit ganz großer Wahrscheinlichkeit keine Kollision haben,
also bezüglich H2. Und dann würde man unterschiedliche Positionen verwenden.
Das wäre also gut. Und dadurch hat man eben
kein primäres und auch insbesondere kein sekundäres Clustern.
Also so sollte man es möglichst machen. Und hier sehen sie
zwei Beispiele von so Funktionspaaren, die genau diese Eigenschaft haben,
dass sie sich für doppeltes Hashing eignen.
Eine mit einer Primzahl als Tabellengröße und eins mit einer Zweier,
ein Paar von Funktionen, was gemacht ist für eine Tabellengröße,
die eine Zweierpotenz ist.
So, jetzt, das war die Stelle,
bis dahin sind wir ungefähr gekommen und jetzt stellt sich die Frage,
wie lange dauert das Ganze?
Ja, jetzt müssen wir das noch analysieren. Also wir wissen noch gar nicht,
wie lange dauert jetzt die Suche in einer solchen Hash-Tabelle,
die mit Adressierung, die mit offener Adressierung gebaut wird.
Und ich kann Ihnen schon sagen, dass vorausgesetzt,
die haben jetzt nicht alles schon vorgearbeitet, eine Überraschung kommt.
Also etwas, wo man, wollte man intuitiv wahrscheinlich
nicht rechnet, also ich hätte damit nicht gerechnet intuitiv
So, wir beginnen mal. Wir müssen wieder ein paar
Annahmen machen, damit die Analyse überhaupt handhabbar wird für uns.
Und unsere Annahmen sind, wir gehen davon aus,
dass die Komplexität nur in Abhängigkeit von dem Belegungsfaktor ist,
ja, von Alpha, also wie viel Prozent der Hashtable belegt ist.
Wir wissen schon, diese Annahme ist streng genommen falsch,
weil wenn wir einfügen und löschen, markieren wir Herr Felder als deleted.
Und diese Felder, die mit der Markierung deleted sind,
die wirken sich auf die Suchzeit aus.
Das haben wir in der letzten Vorlesung gesehen, ja,
und die sind aber ein Belegungsfaktor nicht erfasst,
weil da ist ja nur gespeichert,
wie viele Werte Also wie viel Prozent durch belegte Werte enthalten sind.
Also das ist schon eine Unschärfe, die wir hier jetzt akzeptieren.
Und das Zweite, was wir machen, ist,
wir betrachten alles als möglichst gleich verteilt.
Also die Wahrscheinlichkeiten sollen alles möglichst gleich verteilt sein,
damit wir gleich mit ein paar Annahmen überhaupt die Gleichung lösen können.
Das heißt, wir gehen von gleichmäßigem Hashing aus.
Das heißt, alle Sondierungssequenzen, die überhaupt vorkommen können,
sind gleich wahrscheinlich. Hier ist ein Hinweis,
ich habe Ihnen zwei Schemata ja gezeigt für das doppelte Hashing
Die würden diese sehr strenge Anforderung noch nicht erfüllen. Also nur,
damit sie wissen, dass das nicht irgendwas ist, was klar ist.
Also es ist schwer, diese Forderung wirklich zu erfüllen.
So, okay, die Laufzeit bei der Suche hängt davon
ab, wie viele Sondierungsschritte wir brauchen.
Der Rest ist konstant. Das ist nur eine Schleife über die Sondierungsschritte.
Und wir müssen rausfinden, wie viele Sondierungsschritte brauchen wir eigentlich?
Wie vorher unterscheiden wir wieder die Erfolglose von der erfolgreichen Suche?
Das hängt wieder damit zusammen,
weil bei der erfolgreichen Suche können wir wieder erwarten,
dass wir bei so einer Sondierungssequenz, dass irgendwann mal in der Mitte finden.
Deswegen müssen wir das anders abschätzen, bei der erfolglosen Suche wissen wir,
dass wir die Folge immer bis zum Ende laufen müssen.
Also es ist jetzt damit schon klar, die erwartete Zeit
für die erfolglose Suche ist größer. Das wird teurer,
weil wir die folgen immer bis zum Ende laufen müssen.
Okay, was kommt da raus? Also hier ist erstmal
das Ergebnis für eine Hash-Tabelle mit offener Adressierung und Belegungsfaktor Alpha,
der muss natürlich kleiner sein als eins,
ist die erwartete Anzahl der Sondierung bei Erfolgloser Suche unter der
Annahme des gleichmäßigen Hashings,
höchstens eins
durch eins
minus Alpha
Alpha war der Belegungsfaktor. Was wirkt auf sie jetzt relativ klein?
Das täuscht ein bisschen, weil das Alpha ja auch kleiner ist als eins.
Es kann doch eine größere Zahl werden hier, dadurch,
dass wir einfach diesen Kehrwert bilden.
Ich werde Ihnen am Ende ein paar Beispiele zeigen.
Jetzt leben wir erstmal mit dieser Zahl und wir müssen mal gucken,
warum das da rauskommen kann. Hier ist die Argumentation,
der Beweis, woran man erkennen kann, dass das so ist.
Wir müssen quasi die Sondierungsschritte analysieren und zählen,
wie viele Sondierungsschritte nötig sind.
Und dabei gehen wir
wie folgt
Vor. Wir sagen also als erstes mal, jede Sondierung,
außer die letzte, greift auf einen belegten Platz zu.
Klar. Also wir beginnen mit der,
wir suchen das Objekt, wir springen an irgendeine Stelle,
da steht irgendwas Falsches, da muss ja was anderes stehen,
weil wir machen ja eine erfolglose Suche,
das Objekt kommt ja gar nicht vor.
Dann ist das eine,
war das der erste Sondierungsschritt und dann machen wir in der nächsten Sondierungsschritt,
da sehen wir dann wieder ein anderes Objekt,
was wir nicht suchen und dann wieder und dann wieder.
Und irgendwann finden wir Neil und dann wissen wir,
das Objekt ist nicht da. Also das heißt,
jeder Sondierungsschutz greift immer auf einen belegten Platz zu und der letzte,
der greift auf einen Platz zu,
der ist nie und dann wissen wir,
das Objekt kommt nicht vor
So, wir bezeichnen mit X die Anzahl der Sondierungsschritte,
die müssen wir herausfinden. Genau genommen müssen wir natürlich nicht
X herausfinden, sondern den Erwartungswert von X.
Also wie viele Sondierungsschritte erwarten wir? Und um das zu beschreiben,
schauen wir uns einfach dieses Ereignis an,
dass wir so eine Kollision haben,
also dass wir auf eine belegte Stelle zugreifen mit der Sondierung und noch eine Sondierung machen müssen.
Und das bezeichnen wir mit AI. Also AI ist
das Ereignis, dass es eine ite-Sondierung gibt.
Und die greift wieder auf einen belegten Platz zu. Okay,
so, jetzt müssen wir ein bisschen rechnen und bevor wir das machen,
brauchen wir zwei, wir brauchen zwei so Hilfsgleichungen,
die müssen wir gleich an der richtigen Stelle einsetzen
Diese Folie hat mit dem Beweis überhaupt gar nichts zu tun.
Da sind nur zwei Gleichungen drauf, die wir gleich benötigen.
Deswegen steht die hier. Die erste Gleichung,
die wir benötigen, die hat was mit dem Erwartungswert zu tun,
den wir etwas anders aufschreiben wollen.
Also das ist diese Umformung hier, die gehen wir jetzt gleich durch.
Und die zweite, die können wir auch vorziehen,
ist eine ganz einfache, die zweite Hilfsgleichung ist einfach nur die unendliche,
fallende, geometrische Reihe.
Die geometrische Reihe ist die Summe von I
gleich null
Bis unendlich. X hoch i. Also die Potenzreihe,
also X hoch 1, plus X hoch 2,
plus X hoch 3 und so weiter.
Und wenn der X-Wert vom Betrag ja kleiner als eins ist,
dann wissen wir ja, das X-U-I, das wird ja immer kleiner.
Es wird vom Wert her immer kleiner. Dann wissen wir,
dass diese Summe sich für i gegen unendlich,
also wenn diese Summe bis unendlich läuft,
eins durch eins minus x annähert.
Das ist einfach aus der Mathematik so eine Regel.
Und hier können Sie es
auch nochmal nachlesen
Okay, aber das hier oben ist schon ein bisschen ungewöhnlich.
Also das haben sie mit Sicherheit schon gesehen. Aber das
hier oben ist ein bisschen ungewöhnlicher. Und zwar schauen wir
uns an, wie wir den Erwartungswert von X berechnen können.
Und das Einzige, was wir jetzt voraussetzen, ist,
dass X irgendetwas irgendetwas Diskretes ist.
Das heißt, es nimmt so positive, ganzzahlige Werte an.
Das ist 0-1, 2, 3,
4. Was anderes brauchen wir gar nicht für diese Herleitung hier.
Es ist also irgendein Ereignis, was solche ganzzahligen Werte anhält.
Und dann wissen wir, so ein Erwartungswert, den können wir einfach ausrechnen.
Das ist die Summe über die möglichen Ergebnisse,
die da rauskommen können,
mal die Wahrscheinlichkeit
Wenn man das erste Mal Erwartungswerte anguckt,
dann macht man das immer mit einem Würfel.
Da würde man sagen, so ein Würfel, das ist diese Situation.
Wir haben I gleich eins bis sechs, das sind die
möglichen Ergebnisse. Und dann steht hier die erwartete Würfelzahl,
ist dann nichts weiter als die Würfelzahl, also eins,
mal die Wahrscheinlichkeit, dass die eins geworfen wird,
plus zweimal die Wahrscheinlichkeit, dass die zwei geworfen wird und so weiter.
Das ist einfach die Formel für den Erwartungswert. Das ist
noch nichts Besonderes. So, und jetzt machen wir nur
ein bisschen Mathematik und Tricks ein bisschen rum, um das umzuformulieren. Was wir machen ist,
wir sagen, okay, also hier steht ja ein gleicher Zeitpunkt,
X gleich I, dann können wir auch sagen,
Wahrscheinlichkeit, dass wir genau das Ereignis I haben,
können wir auch beschreiben, als das Ereignis,
dass X größer gleich I ist, minus, das Ereignis,
dass das X größer gleich I plus 1 ist.
Da bleibt genau das Ereignis I eben über. Und das
können wir so beschreiben. Dann können wir diese Summe zerlegen,
dann haben wir hier eine Summe von Igarch 1 bis M,
I mal die Wahrscheinlichkeit, dass X größer gleich I ist,
minus hier die Summe von Igarch 1 bis MI mal die Wahrscheinlichkeit,
dass das X größer gleich I plus 1 ist.
Diese Summenform hier ist eine ganz spezielle Form,
das ist eine sogenannte Teleskopsumme, für diejenigen, die noch Amateurfunker sind.
Die haben vielleicht noch eine Teleskop-Antenne oder vielleicht haben sie noch
ein Radio, das ist natürlich ein Teleskop-Antenne.
Der Grund, warum das Teleskop Summe heißt, ist,
dass die Sumanten hier immer um eins verschoben sind gegenüber den Sumann hier.
Und dadurch, dass wir die subtrahieren,
schiebt sich diese Summe wie so eine Teleskop-Antenne zusammen.
Und wir haben nur zwei Werte in Anfangs- und den Endwert.
So, damit wir das machen können, verschieben wir erst mal diese Summe ein bisschen.
Also die geht jetzt, der vordere Teil ist gleichgebildet,
von I gleich zwei bis M plus eins.
Und hier haben wir I minus eins,
mal die Wahrscheinlichkeit, dass X größer gleich I ist.
Ja, was wir also gemacht haben hier ist,
wir haben quasi den Index in der Summe, um ein Feld verschoben,
weil hier innerhalb dieser Wahrscheinlichkeit können wir nicht so viel mathematisch tun.
Und da wollen wir gerne,
dass da das Gleiche steht wie hier,
haben wir
es alles um eins verschoben
Okay, so, jetzt können wir mal überlegen,
also wenn das I gleich eins ist,
dann würde ja hier eins,
minus eins, da steht hier ja null, ja,
und wenn da null mal irgendwas steht, ist das null.
Also ist das egal, ob die Summe bei zwei oder bei eins anfängt.
Also fangen wir wieder bei eins an, ne, eh gleich eins.
So, wenn hier M plus eins steht,
dann würde stehen M plus eins, minus eins.
Es wäre also der Ämte-Sumant, ja, und der ist,
der ist auch wieder null.
Wobei ich jetzt gerade gar nicht weiß,
Okay, also der ist auch wieder null, muss ich mal gerade drüber nachdenken.
Gehen wir mal gerade davon aus, der ist null,
ja, also der ist auch wieder null,
deswegen können wir diese Summe bis M laufen lassen. Und ja,
der ist deswegen null, Entschuldigung, ich habe auch manchmal so,
der ist deswegen null, weil die Wahrscheinlichkeit,
dass X größer gleich M plus 1 ist, natürlich null ist,
weil der Wert X liegt ja im Wertebereich von 1 bis M,
ja, ist klar, ne, oder?
Ja, hoffentlich, ne, so. Okay, deswegen ist das natürlich toll. Okay,
so,
dann haben wir hier wieder diese Summe und jetzt können wir
die gleichen Summanten voneinander abziehen und was dann übrig bleibt,
also wir sehen hier,
wir haben ja immer die mal die Wahrscheinlichkeit und hier haben wir eh minus eins mal die Wahrscheinlichkeit,
bleibt also immer genau eins übrig.
Und dann haben wir hier also die Summe von Igarch 1
bis M die Wahrscheinlichkeit, dass x größer gleich eh ist.
So ist einfach nur eine Umformung, dieses Erwartungswert für dieses spezielle Ereignis.
Und das hilft uns in dem Augenblick, wo wir Schwierigkeiten haben,
zu berechnen, wie groß die Wahrscheinlichkeit ist für ein spezielles Ereignis.
Es ist aber leichter ist zu berechnen, wie groß die Wahrscheinlichkeit ist,
dafür das Ereignis eben größer gleich eines speziellen Verses.
Also wenn wir so eine Situation haben,
dann hilft uns natürlich
diese Umformung
Das werden wir halt noch gleich brauchen. Okay,
also betrachten Sie das einfach als so ein Umformungsding und
wir werden einfach für den Erwartungswert diese Formel benutzen können.
Und die werden wir natürlich auch benutzen.
So, das war eigentlich schon der schwierige Teil.
Die Laufzeit jetzt zu beweisen, ist einfacher, ja. Genau,
leider funktioniert die, müsste es doch einfach wahrscheinlich gleich möglich,
dass wir nichts größer wird, etwas Eises und das Leben überfallen werden.
Genau. Ja, ja, genau. Und ich glaube,
das habe ich gerade auch noch gesagt,
aber also wenn das noch nicht alle so mitgekriegt haben,
also hier die Wahrscheinlichkeit, x größer gleich I,
wenn wir IM plus 1 einsetzen, ist die Wahrscheinlichkeit null,
weil das x nicht größer gleich M sein kann.
Das stimmt
Okay, gut. So, jetzt müssen wir diese Wahrscheinlichkeit abschätzen.
Also nur noch mal, damit Sie jetzt wissen, wo wir sind.
Wir gucken noch mal kurz, das wollten wir beweisen.
X, unser X ist die Anzahl der Sondierungen, die wir machen müssen.
Und AI ist das Ereignis, dass die IT-Sondierung da war,
dass wir die gemacht haben und die war noch erfolglos.
Also das heißt, die hat auf einen belegten Platz zugegriffen.
Gut, wenn Sie das nicht mehr lesen können,
dann sagen Sie Bescheid.
Ich weiß nicht, wann ich hier das Ding runtermachen muss.
Noch geht es, glaube ich, so gerade.
Okay. So, okay
So, die Wahrscheinlichkeit, dass wir mehr als I-Sondierungsschritte brauchen.
Wann tritt das auf? Das tritt auf,
wenn wir, wenn A1 eintritt, also der erste Sondierungsschritt,
haben wir gemacht und hat auf ein nicht belegtes Element,
auf ein belegtes Element zugegriffen.
Und A2 ist eingetreten, ja, also beim zweiten Sondierungsschritt
haben wir auf ein belegtes Element zugegriffen und A3s eingetreten,
beim dritten Versuch haben wir auf ein belegtes Element zugegriffen.
Und A4, A5, A6 und so weiter,
Und beim Aiden-Versuch haben wir auf ein Element zugegriffen,
was frei war, also was nicht belegt war.
Und deswegen endet da die Sondierungsfolge. Das beschreiben wir einfach
durch so eine Schnittmenge, das Ereignis AK.
Wir bilden einfach den Schnitt, die müssen alle erfüllt sein.
Das ist so wie ein logisches Und an der Stelle.
Okay, so, wie können wir das jetzt ausrechnen?
Wir machen das mit so einem Trick. Wir sagen dann,
okay, was ist denn das hier eigentlich für eine Wahrscheinlichkeit?
Das ist die Wahrscheinlichkeit, dass A1 eintritt.
Das ist das Erste. Also beim ersten Mal greifen wir
auf ein belegtes Element zu
Mal die Wahrscheinlichkeit, dass A2 eintritt. Jetzt könnte man sagen,
okay, klar, mal die Wahrscheinlichkeit des A3 eintritt und so weiter.
Ja, aber da gibt es noch eine spezielle Besonderheit.
Wir müssen gucken, was ist die Wahrscheinlichkeit,
dass A2 eintritt unter der Voraussetzung, dass vorher schon A1 eingetreten ist.
Weil nur dann machen wir A2. Und deswegen steht hier,
also das ist hier die Voraussetzung, das ist die bedingte Wahrscheinlichkeit,
das schreibt man mit den Balken. A1, so,
und dann hätten wir hier die Wahrscheinlichkeit, dass A3 eintritt,
also dass wir beim dritten Sondierungsversuch keinen Erfolg haben,
unter der Voraussetzung,
dass A1 und A2
eingetreten sind
Und so weiter und so weiter. Und dann steht dann
hier am Ende der letzte Faktor, das ist die Wahrscheinlichkeit,
dass AI minus 1 eintritt unter der Voraussetzung des vorher auch alle Ereignisse eingetreten sind.
So, einfach nur ein bisschen logischem Denken kann man sagen,
wie groß diese Wahrscheinlichkeiten sind.
Die erste Wahrscheinlichkeit ist N durch M, der Belegungsfaktor.
Wie groß ist die Wahrscheinlichkeit, dass ich beim ersten Zugriff
ein belegtes Feld erwische? Naja, wenn ich in meiner,
wenn ich einen Belegungsfaktor von Alpha habe oder von N durch M
, dann sind NDRM-Felder belegt.
Und wenn ich da zufällig reingreife, dann ist die Wahrscheinlichkeit gerade N durch M.
Dass ich da eins erwische, was billig ist.
Also die Wahrscheinlichkeit, dass ich beim ersten Mal ein belegtes Element erwische,
ist gerade N durch M. So, die Wahrscheinlichkeit,
dass ich beim zweiten Mal ein belegtes Feld erwische,
unter der Annahme des A1 eingetreten ist, das können wir so vorstellen,
wir hatten vorher einen Array, der Größe M.
Und darin waren Enfelder belegt. So, und was wir dann machen,
ist ganz einfach, Also das ist die Ausgangssituation.
Wir haben hier M-Felder und von denen waren Enfelder irgendwie belegt.
Ja, die sind die Belegten. Ja, das sind
Ennen viele. Und wir haben jetzt gesagt, okay,
wir haben jetzt das Ereignis A1 ist eingetreten.
Also beim Ereignis A1 haben wir auf so ein belegtes Feld zugegriffen.
Wir wissen, beim nächsten Schritt bei der nächsten Sondierung
greifen wir auf ein anderes Feld zu.
Nicht mehr auf dieses, weil die Sondierungsfolge greift ja auf
jedes Feld nur einmal zu.
Ja, das heißt, wir haben jetzt noch minus
eins viele Möglichkeiten
Wo wir gucken müssen, wo wir gucken können.
Also der nächste Sondierungsschritt ist irgendein anderes Feld, nur nicht das.
Und das sind M minus eins viele. So,
und wie viele von diesen M minus eins fielen sind belegt,
N minus eins viele,
weil dieses Feld ist ja belegt und vorher hatten wir N belegte,
also eins weniger, bleibt dann auch übrig für den ganzen Rest.
Deswegen ist das N minus eins durch M minus eins. Okay,
und so geht es dann weiter,
wenn wir dann den zweiten,
also angenommen die zweite Sondierung ging jetzt auf dieses Element,
dann bleiben noch M minus zwei Felder übrig und von denen sind N minus zwei Felder belegt.
Und dann können wir die aufmultiplizieren und kommen dann am Ende
auf diese Summe hier
Und jetzt sind wir ganz großzügig,
das schätzen wir ab und sagen, okay,
alle diese so Mann sind also kleiner als N durch M.
Und deswegen ist das kleiner gleich N durch M hoch i minus 1. Okay,
ja. So, und N durch M ist Alpha,
also ist das gleich Alpha hoch i minus 1.
So, und jetzt sind wir eigentlich fertig.
Jetzt nehmen wir unsere Formel, hier ist der Erwartungswert,
Erwartungswert ist die Summe über alle möglichen Ereignisse,
die gleich 1 bis M. I,
also was dann rauskommt,
also Sondierungsfolge,
hat die Länge I. Mal
Die Wahrscheinlichkeit, dass dieses Ereignis eintritt, dass also die Sondierungsfolge die Länge ihr hat.
Das ist die erwartete Länge. Und dafür haben wir
eben mit unserem kleinen mathematischen Zaubertrick eine andere Formel gefunden.
Das ist das Gleiche, wie die Summe von e gleich 1 bis M,
die Wahrscheinlichkeit, dass das x größer gleich e ist.
Und das haben wir gerade abgeschätzt. Das können wir da einsetzen. Da sehen wir hier,
dass es kleiner gleich,
die Summe von e gleich 1 bis eigentlich M,
aber wir sind großzügig,
wir lassen es bis unendlich laufen,
von Alpha hoch i minus 1. Dann schieben wir den Index noch um eins nach unten,
dann ist es die Summe von e gleich 0 bis unendlich
von Alpha
Und das ist die geometrische Reihe. Wir wissen,
das Alpha ist ja kleiner als eins. Der Belegungsfaktor kann
höchstens eins sein. Und deswegen dürfen wir hier die geschlossene Form einsetzen.
Und das ist eins durch eins, minus Alpha.
Also da sehen wir jetzt die Anzahl der Schritte.
So, das ist der mathematische Teil. Aber so eine
mathematische Herleitung ist immer ein Ding. Viel wichtiger ist,
dass man irgendwie versteht, warum das da eigentlich rauskommt.
Also warum ist es eins durch eins, minus Alpha?
Können wir uns das auch vielleicht algorithmisch irgendwie vorstellen,
warum das da rauskommt?
Wir können das interpretieren. Dafür nehmen wir nicht diese Formel,
sondern wir gehen mal einen Schritt zurück.
Wir gehen mal zu dieser Summe hier
Und dann würden wir sagen,
die erwartete Anzahl von Sondierungsschritten ist kleiner gleich eins, plus Alpha,
plus Alpha-Kabarat, plus Alpha hoch drei und so weiter.
Und wenn man das mal so aufschreibt, dann könnte man sagen,
okay, also wir wissen, eine Sondierung findet immer statt.
Wir müssen erstmal einmal schauen auf das erste Element.
Das passiert garantiert. Die nächste Sondierung findet mit einer Wahrscheinlichkeit von ungefähr
Alpha statt. Wie zweite Sondierung. Nämlich immer dann,
wenn beim ersten Mal wir ein belegtes Feld erwischt haben und
dafür ist die Wahrscheinlichkeit Alpha. So,
die dann folgende Sondierung findet ungefähr mit Wahrscheinlichkeit Alpha Quadrat statt,
weil wir vorher schon zwei Sondierungen gemacht haben,
die alle die Eigenschaft hatten, dass sie zu einem belegten Feld geführt haben.
Und so geht das dann weiter. Und es ist einfach
auch drei, einfach für und so weiter.
Und das ist genau diese Summe, die wir hier gesehen haben.
Und die führt uns zu dieser geschlossenen Formel. Okay,
also so kann man sich, kann man sich ungefähr plausibel machen,
warum diese Formel da rauskommt.
So, und jetzt ein paar Beispiele, die sind ganz instruktiv.
Also wir gehen mal davon aus, wir haben eine Hesh-Tabelle,
wir haben Belegungsfaktor von
Das bedeutet, die Hash-Tabelle ist 50 Prozent voll.
Die Hälfte der Felder hat eine Zahl gespeichert, die andere Hälfte ist frei.
Ja, was würden wir dann erwarten? Ja,
so geschätzt würde man sagen, naja,
mit ein bisschen Glück, 50-50 trifft man beim
ersten Mal ein freies Feld und dann ist sofort Schluss.
Mit 50-50 Wahrscheinlichkeit trifft man aber auch ein
belegtes Feld und da muss man noch eine Sondierung machen.
Und dann würde man wahrscheinlich sagen, ja, aber mit größerer Wahrscheinlichkeit,
spätestens beim zweiten Mal habe ich dann aber auch ein freies Feld getroffen.
Und das ist auch das, was aus der Formel rauskommt.
Also wenn wir jetzt das Alpha 0,5 einsetzen,
dann sehen wir,
die erwartete Anzahl von Sondierungen ist kleiner gleich eins durch 1
minus 0,5
und das ist zwei
Also kleiner gleich zwei. Es ist nicht zwei, sondern kleiner nicht zwei.
Okay, so, jetzt wird unsere Hash-Tabelle voller.
Wir schreiben da Daten rein und Daten und dann.
Jetzt wird es unangenehm, weil wenn die Tabelle voll ist,
brauchen wir natürlich lange für eine erfolglose Suche,
weil wir treffen immer wieder belegte Felder.
Ja, aber da steht ja nie unsere Zahl drin, die wir suchen.
Die kommt ja nicht vor. Wir machen ja eine erfolglose Suche. Okay,
also wenn wir 90 Prozent erreicht haben, ja,
dann ist die Hash-Tabelle eben zu 90 Prozent voll mit Werten besitzt.
Und wenn wir die 90 Prozent hier einsetzen,
also eins durch 1 minus 0 Komma, dann kommt da 10 raus, ja.
Auch das, würde ich sagen, kann man intuitiv sagen,
das kann man sich gut vorstellen. Also wenn ich sage,
in neun von zehn Feldern ist was drin,
dann brauche ich ungefähr so zehn Versuche,
um ein Feld zu erwischen, was frei ist.
Also auch das ist intuitiv klar. Und damit ist auch genauso klar,
dass wir hier bei 99 Prozent Füllstand,
jetzt ist sie wirklich sehr voll, entsprechend auch schon 100 Sondierungsschritte brauchen,
ja, um zu gucken, wo das Element steckt.
Okay, so, das ist also die Anzahl der Sondierungsschritte für die erfolglose Suche.
Und daraus können wir sofort ableiten, wie lange unsere Inside-Operation braucht,
weil die setzt ja eine erfolglose Suche voraus.
Die geht mit einer erfolglosen Suche los.
Und sobald die eine freie Position gefunden hat,
fügt sie das Element an der Stelle ein.
Deswegen braucht die auch genauso viele Sondierungsschritte, eins durch eins minus Alpha.
Und das ist dann auch die Zeit, die wir brauchen.
Die erwartete Laufzeit für das Einfügen eines Elements.
Also Hash Insert benötigt konstante Zeit für jeden Sondierungsschritt,
plus konstante Zeit für das Einfügen des Elements.
Und wir wissen, wir haben eins durch eins minus
Alpha Sondierungsschritte im mittleren Fall.
Okay, so, so war es
so gut
Jetzt kommen wir zu dem Punkt. Bis dahin sollte es eigentlich noch wenig überraschend sein.
Also mit so ein bisschen gesunden Menschenverstand, auch ohne Mathematik,
sollte man so, das habe ich gerade versucht, ihnen klarzumachen, das eigentlich rauskriegen.
Das sollte man erwarten. Ich hätte das jetzt erwartet, dass sowas da rauskommt. So,
wenn ich jetzt einen Schritt weiter denke,
an die Erfolgreiche suche, jetzt ist das Element drin,
da haben wir bei der Suche mit Verkettung gesehen,
dass wir ungefähr so einen Faktor ein Halb eingespart haben,
weil so im Schnitt haben wir in der Mitte der Folge natürlich das Element gefunden,
manchmal weiter vorne, manchmal weiter hinten,
aber im Schnitt so ungefähr in der Mitte.
Wenn man das hier darauf anwählt, dann würde man sagen,
ja, also eine erfolgreiche Suche, ja,
die müsste dann hier vielleicht so fünf Schritte brauchen,
hier vielleicht 50 oder so
Es wäre das, was ich persönlich erstmal
so intuitiv finden würde, was man da erwarten würde.
Und wir werden sehen, dass das so nicht ist.
So, hier ist erstmal die Formel.
Für eine Hash-Tabelle mit offener Adressierung und Belegungsfaktor Alpha
ist die Anzahl der erwarteten Sondierung bei erfolgreicher Suche.
Also jetzt finden wir das Element, es kommt tatsächlich vor.
Unter den Annahmen erstens, gleichmäßiges Hashing,
also alle Fälle, alles immer gleich wahrscheinlich.
Und zweite Annahme, gleiche Wahrscheinlichkeit für den Suchschlüssel.
Also wir suchen nach irgendeinem der gespeicherten Schüssel mit gleicher Wahrscheinlichkeit.
Unter den beiden Annahmen ist die Anzahl höchstens eins durch Alphamal,
der natürliche Logorithmus von 1 durch 1 minus Alpha.
Oh Gott, oh Gott. Ja, das ist erstmal die Formel,
die da rauskommt und am Ende werde ich Ihnen wieder ein paar Zahlenbeispiele liefern.
Wahrscheinlich kann keiner von Ihnen jetzt mal schnell ausrechnen,
was da so rauskommt für 0,5 oder so.
Kannst du einen Taschenrechner tippen, aber Sie können auch noch
ein paar Minuten warten, dann zeige ich Sie.
So, erstmal müssen wir jetzt gucken,
woher kommt das, wie kann man das ausrechnen,
dass das so ist
Um das zu sehen, macht man einen ganz geschickten Schachzug.
Also es ist eigentlich ein ganz eleganter Beweis. Wir brauchen
gar nicht viel mehr als das, was hier steht.
Wir überlegen uns einfach, was ist eigentlich passiert zu dem Zeitpunkt,
wo wir das Objekt eingefügt haben, was wir jetzt suchen.
Ja, wir suchen jetzt ein Element mit dem Schlüsselwert K.
Wir betrachten aber gar nicht den Zeitpunkt,
wo wir es suchen, sondern wir betrachten mal den Zeitpunkt,
wo wir dieses Element eingefügt haben.
Weil als wir es eingefügt haben,
hatten wir eine erfolglose Suche und da kennen wir ja schon
die Anzahl der Sondierungsschritte, die wir gebraucht haben.
Also, wir suchen jetzt also einen Schlüssel K und
wir nehmen jetzt mal an
Das ist der I plus erste Schlüssel bezüglich der Eingabereihenfolge,
der Einfügereihenfolge. Wir haben also jetzt,
wir wissen ja, wir haben N-Schlüssel in der Hash-Tabelle,
die haben wir in irgendeiner Reihenfolge eingefügt.
Und das I sagt uns einfach, in welcher Reihenfolge.
Das ist das erste Element 1 bis 2,
das ist als drittes als fünftes, haben wir es eingefügt.
Okay, so, als wir das eingefügt haben,
haben wir eine Erfolglose Suche gemacht, um es einzufügen.
Und wir kennen die Länge der Sondierungsfolge. Das ist also hier
die erwartete Anzahl von Sondierungsschritten für die Suche des Schlüssels Cars,
war ja eins durch 1 minus Alpha erstmal,
eine Belegungsfaktor
Und jetzt ist nur noch die Frage,
was hatten wir denn zu dem Zeitpunkt für einen Belegungsfaktor?
Der ändert sich ja, wir haben die Elemente nach und nach eingefügt.
Am Anfang war der Belegungsfaktor offensichtlich null, da war noch gar nichts drin.
Und dann war der Belegungsfaktor eins durch M,
haben es ein Element eingefügt, dann bei der Belegungsfaktor 2 durch M,
3 durch M4, durch M mit jedem Element,
was wir eingefügt haben, steigt der Belegungsfaktor an.
Das heißt, als wir das iPlus erste Element eingefügt haben,
war der Belegungsfaktor iDurch M, weil E-Elemente waren schon in der Tabelle drin.
Und das ist jetzt das iPlus erste Element. Gut,
das heißt, das können wir hier einsetzen und dann müssen wir
die erwartete Anzahl von Sondierungsschritten für dieses Element K ist eins durch eins minus I durch M.
Und ja, damit sind wir fast fertig mit diesem Argument,
weil jetzt müssen wir nur noch einen Mittelwert bilden über alle Elemente,
weil wir sagen, die Wahrscheinlichkeit,
dass wir für einen speziellen Suchschlüssel ist gleich verteilt.
Also wir suchen nach jedem Schlüssel mit der gleichen Wahrscheinlichkeit,
insbesondere ist das unabhängig von dem Zeitpunkt, wo wir das eingefügt haben.
Okay, so, also Mittelwert bilden,
also der Erwartungswert für die Anzahl der Sondierungsschritte,
Mittelwert ist eins durch N, mal die Summe,
für alle möglichen Schlüsselwerte,
I gleich 0
bis N minus
Also wir summieren hier über den Einfügezeitpunkt. Und dann wissen wir,
die Anzahl der Sondierungsschritte, was jetzt die Anzahl der Sondierungsschritte sind,
die wir bei der Suche brauchen, um das Element zu finden,
ist eins durch eins minus i durch M.
Okay, das ist so ein bisschen unschön hier.
Wir müssen das jetzt umformen. Wir multiplizieren den Zähler und den Nenner mit M.
Dann steht hier also M durch m-i und dann ziehen wir
das M erstmal schon mal aus der Summe raus.
Dann haben wir hier M durch M mal die Summe von
i gleich 0 bis N minus 11 durch m-i.
M durch N ist der Kehrwert des Belegungsfaktors. Das ist
eins durch Alpha. Also eins durch Alpha ist das M durch N.
Und hier in der Klammer habe ich ihn jetzt mal so mathematisch unprofessionell,
dass sozusagen mal so hingeschrieben, ja, mit so Pünktchen.
Das ist also offensichtlich eins durch M,
wenn das I gleich null ist und dann ist es eins durch M minus eins,
wenn das I gleich eins ist und so weiter.
Und dann der letzte Sumann ist eins durch M minus N
plus 1. Und jetzt können wir einfach die Summe andersrum bilden.
Wir summieren einfach in einer anderen Reihenfolge. Wir summieren jetzt
von hinten nach vorne auf, dann ist nämlich die Summe ein bisschen einfacher. Was wir machen ist,
wir fangen mit dem Element an und sagen jetzt,
okay, das erste Zahl, die wir summieren,
ist eins durch M minus N plus 1,
also eins durch I,
wenn die Summe bei M minus N plus 1 beginnt und
dann nehmen wir den so Mann und den und den und den und gehen,
also von hinten nach vorne.
Wir drehen also die Summe einfach nur einmal rum.
Und dann haben wir die Summe von m-n-n-n-n-plus-1 bis M1 durch I.
Wir haben es einfach andersrum aufsummiert.
Das ist der Trick dabei. Ja,
und hier ist nur einmal so hingeschrieben,
damit sie sich das vielleicht merken können oder vorstellen können.
Okay, so, die Summe können wir jetzt auseinanderziehen und sagen,
okay, wir nehmen mal den ganzen Bereich,
wir gehen mal von I gleich 1 bis M und davon ziehen wir einfach diese ersten alle wieder ab.
Ja, also uns war alles von I gleich 1 bis m-n-n,
das ziehen wir wieder ab und dann würden wir bei M-n-n-plus-1 beginnen.
Ja, das ist also das Gleiche hier
Okay, diese Zahl hier, also die Summe über
die Brüche, die kennen die Mathematiker sehr gut.
Die Summe über die Brüche sind die harmonischen Zahlen,
so heißen die. Also wenn sie eins plus einhalb,
plus ein Drittel, plus ein Viertel, plus ein Fünftel,
das sind die harmonischen Zahlen, die daraus kommen,
wenn sie das ausrechnen. Okay, können wir also,
wenn wir das mit HM bezeichnen, können wir sagen,
das ist eins durch Alpha, HM-HM-N-N wäre das jetzt.
Also das wäre die Ämter harmonische Zahl,
alle Brüche bis eins ein Ämtel und das wäre die M-N-Ente harmonische Zahl,
also alle Brüche addiert bis ein M-Entel,
wenn sie so wollen
Ja, so können wir das aufschreiben. Von da kriegt man auch das Ergebnis,
was wir gleich brauchen, aber wir machen das einfach ein bisschen anders.
Wir fangen hier nochmal an und bei dieser Summe hier und
wir verwenden so einen Standard-Trick, den man mal gut machen kann.
Was wir tun, ist ganz einfach, wir sagen, okay,
wir haben hier eine Summe und wir approximieren diese Summe einfach durch das Integral.
Also eine Summe ist ja überdiskrete Werte und stattdessen nehmen wir
das Integral mit diesen Grenzen. Und das kann man machen,
das kann man sich hier so vorstellen,
wenn Sie sich angucken, hier wäre so eine Funktion,
also wir haben ja sowas wie eins durch X
Das ist ja ungefähr so, wird das verlaufen.
Und wenn wir eine Summe von diskreten Werten haben,
dann nehmen wir sozusagen immer so Balken.
Also uns so nehmen wir das auf. Und was wir damit beschreiben,
wenn wir das immer noch mit eins multiplizieren,
mit der Breite dieses Balkens, ist quasi die Fläche hier unter diesen Balken.
Und wir können genauso gut die Funktion hier nehmen.
Es ist jetzt vom Beispiel her ein bisschen unschön gezeichnet,
also weil wir das ja nach oben abschätzen.
Also die Linie müsste eigentlich hier oben lang gehen.
Also wir überschätzen das ein bisschen, wenn wir das Integral verwenden.
So ein Integral können wir immer benutzen,
um eine Summe abzuschätzen
Okay, und ein Integral können wir auflösen, also meistens,
oder manchmal müssen wir nur integrieren und vielleicht können Sie sich noch daran erinnern,
das war immer so eine der fiesen Sonderregeln in der Mathematik-Oberstufe,
Integral von 1 durch X, ist der natürliche Logorithmus.
So, und dann muss man nur die obere und unsere Grenze einsetzen und
dann haben wir hier stehen, Logorithmus von der oberen Grenze m,
minus natürlicher Logorithmus von der unteren Grenze m-n. Ja,
so, und jetzt brauchen wir noch ein bisschen Logorithmenregeln,
dann weiß man hier minus,
das ist das Gleiche wie ein Quotient,
das heißt,
das ist der Logorithmus von M durch m-n und jetzt können Sie
den Zähler und den Nenner wieder durch M dividieren und dann haben Sie hier eins stehen und hier haben Sie eins minus N durch M und es ist Alpha.
Also das heißt, das Ergebnis ist eins durch Alpha mal,
der Logerhithmus von 1 durch 1 minus Alpha.
Das ist die Anzahl der Sondierungsschritte, die wir brauchen bei einer erfolgreichen Suche.
Okay, und jetzt kommt das Famose an der ganzen Geschichte.
Wir setzen mal Zahlen ein, was da rauskommt.
Also wir gehen mal wieder von 0,5 aus.
Bei 0,5 kommt noch das, was man erwarten würde.
Es ist ein bisschen schneller, als die Erfolglosesuche.
Weil wir haben ja die Wahrscheinlichkeit, dass wir
das am Anfang treffen mit dem ersten ersten Mal nachgucken.
Und es kommt da auch raus, also wir hätten jetzt zwei,
also eins durch Alpha, eins durch ein halbes zwei,
mal den Logorithmus von 1 durch 1 minus 1,5,
also 1 durch 1,5 bis 2,
also zweimal Logorithmus von 2. Natürlich Halloghithmus von 2, das ist ungefähr 1,39.
Also du bist ein bisschen schneller als das. Das ist,
würden wir erwarten. Wenn wir 90 Prozent Füllung haben in der Hash-Tabelle,
dann sind 90 Prozent der Felder sind, wie gesagt,
belegt, 10 Prozent frei, dann ist der Erwartungswert,
hier kleiner gleich 10 Neunte, also eins durch Alpha,
10 Leute,
mal Logorithmus und eins durch 1 minus Alpha ist
10. Also vielleicht erinnern Sie, das sind übrigens die Zahlen,
die wir eben hatten. Eben hatten wir eins durch 1
minus Alpha immer als Anzahl der Sondierungsstände. Die stehen jetzt
hier im Logorithmus drin. Und das hilft uns enorm.
So, das heißt 1099. Mal Logorithmus von 100. Das heißt,
selbst bei 99 Grad Füllung der Hash-Tabelle finden wir ein Element
im Schnitt noch nach unter fünf Sondierungsschritten.
Ja? Also das finde ich auch sehr bemerkenswert.
Also wenn man überlegt, die Hash-Tabelle ist so voll,
wenn wir da was einfügen, dann brauchen wir 100 Schritte,
um noch einen Platz zu finden.
Aber im Schnitt, um es zu finden,
kommen wir mit unter fünf Schritten aus
Es sei denn, ich habe mich verrechnet.
Es geht ja los, es ist richtig,
es hilft die Tabelle, aus der ich mich unterschreibe.
Gut, in der anderen Wahl,
dass dort einige Telemente sind, von denen ich beziehe.
Genau. Das heißt, die nicht weiter in Deutschland,
wo ich einfühle, das ist so absurd, das würde schlecht gefallen.
Ja, okay, die Frage für die Publikum,
für das Publikum im Fernsehen war, dass das eben nur dann gilt,
wenn wir keine Delete-Operation machen.
Das stimmt, die ursprüngliche Annahme,
die wir gemacht haben, war eben,
dass die Belegung nur von Alpha abhängt
Und diese gelöschten Felder nicht berücksichtigt werden. Man kann sich das ungefähr so vorstellen,
wenn sie statt Alpha mal nicht den Belegungsfaktor nehmen würden,
sondern sie würden Anzahl angeben,
wie viel Prozent der Felder haben entweder einen Wert oder einen Deleted-Fleck,
dann stimmt die Analyse wieder.
Also dann hängt es einfach nicht mehr davon ab,
wie viele Felder sind da, sondern wie viele haben auch dieses Deleted-Fleck.
Nur das Dumme daran ist, es hängt dann nicht mehr,
wir wollen natürlich in unsere Analyse machen,
in Abhängigkeit von der Eingabe.
Und hier hängt es dann von dieser Sequenz ab und
das ist ganz schwer abzuschätzen,
wie viele Felder haben denn jetzt so
eine Deleted-Markierung
Aber dann würde das wieder stimmen. Also wenn neun,
sie könnten sagen, ich habe vielleicht 40 Prozent der Felder belegt,
aber 99 Prozent der Felder haben so eine Deleted-Markierung,
also inklusive den Belegten, dann hätten sie wieder diese Suchzeit.
Was aber auch zeigt,
dass bezüglich der Suche diese Deleted-Felder auch nicht so schädlich sind,
wie man vielleicht am Anfang vermutet hätte.
Weil die Zeit hier in die Anzahl der Sondierungsschritte nicht so stark steigt.
Okay, kommt eine kleine Zahl raus. Gut,
so, das war die Analyse bei der offenen Adressierung.
Und das hier sollte eine Motivation sein,
darüber nachzudenken, ob man da nicht noch
was rausholen kann
Also wir sind ja so nah an einer fast konstanten Anzahl von Schritten,
die wir brauchen, um ein Objekt zu finden.
Kann man das noch verbessern? Jetzt müssen wir wieder auf
den Algorithmus schauen und es gibt eine Verbesserung dieses Verfahrens.
Die wurde von Brand entwickelt. Und jetzt kommt das zweite Mysterium in
dieser Vorlesung. Ich werde ihn mindestens einen Grund zeigen,
warum sie, zumindest wenn es ums Expertenwissen geht,
wie hier, nicht in Wikipedia schauen sollten.
Also wir reden über Brands. Brand war ein Informatiker,
der sich mit offener Adressierung beschäftigt hat
Hat gesagt, ich habe eine tolle Idee,
wir machen den Inside-Algorithmus ein bisschen anders in der Hoffnung,
dass wir dann die Objekte noch ein bisschen schneller finden können.
Und die Idee von dem Brand,
die ist eigentlich ganz einfach, der hat sich nämlich überlegt,
können das nochmal an dem Bild machen oder angenommen,
hier ist jetzt unser K,
was wir einfügen wollen und für das K hätten wir jetzt so eine Sondierungsfolge.
Und dann würden wir jetzt hier und dann würden wir hier hinkommen und so weiter.
Das wäre die Sondierungsfolge für K. Da werden die immer belegt
und irgendwann hätten wir vielleicht den Schritt nach hier,
da wäre ein freies Feld und da könnten wir
das K einfügen
Was der Brand sich jetzt ausgedacht hat, ist,
der sagt, okay, hier an der Stelle,
da steht ja was anderes, da steht ja das X, ja.
Dieses X hat eine andere Sondierungsfolge, wenn wir doppeltes Hashing machen. Das heißt,
wenn ich für das X nach dem nächsten Feld suche,
wird es nicht dieses Feld sein mit großer, wahrscheinlich.
Es ist irgendein anderes. Es ist vielleicht dieses Feld hier, ja.
Das könnte ja frei sein, wenn das frei wäre,
dann würde ich doch eigentlich Folgendes machen.
Ich nehme einfach das X und schreibe das hierhin,
löscht das hier raus und schreibe hier das Kai,
dann bin ich fertig. Ja, und das Tolle ist,
die Sondierungsfolge für das K ist viel kürzer geworden,
die für das X ist ein Schritt länger geworden.
Okay, das haben wir bezahlt, aber für USK,
die ist jetzt ja viel kürzer geworden
Okay, das ist die grundlegende Idee des Brand-Algorithmus.
Und hier ist es aufgeschrieben, wie das funktioniert.
Wir ändern also die Insert-Funktion. Und die Idee ist,
wir machen also doppeltes Hashing mit zwei Hash-Funktionen.
Und wir betrachten jeweils immer zwei alternative Plätze. Also wir sagen,
okay, wo ist die nächste Position für unser Objekt K?
Das wird in der Variablen JNEX stehen.
Also es wird die nächste Position sein.
Und wo ist die nächste Position für Tee von K,
also die Stelle, wo wir gerade draufschauen.
Und das wird
die alternative Position sein.
Die wird alt
So, okay, wie machen wir das?
Wir fangen an. Tee ist am Anfang null,
Tee ist so ein Zähler für die Sondierungsschritte.
Und J ist die initiale Position, wo wir hingucken.
Ist die erste Hash-Funktion und davon das Karne.
Ist also diese Position. Wo wir hinschauen. So,
und dann ist hier diese Wild-Schleife, die kennen wir eigentlich schon.
Die prüft also so lange, bis wir eine Einfüge-Position gefunden haben.
Also solange das TJ nicht Nil und nicht Deleted ist,
können wir es nicht einfügen. Und solange das T
kleiner ist als M, werden wir das noch probieren.
Okay, wir gehen in die nächste Runde.
Wir sagen Tee ist gleich Tee plus 1.
Wir berechnen die Jotten
Next. J. Next ist einfach J. Plus. Die Sprungweite
. H2 von K. Und dann Modulo M. Das ist jetzt
ein bisschen anders aufgeschrieben als eben, als die erste Insight-Funktion.
Da hatten wir nämlich hier mal J. Plus T mal oder E mal.
Hier ist es so, dass wir das J immer vergrößern,
in jedem Schleifendurchlauf. Es kommt also aufs Gleiche raus.
Okay, so, das wäre die Position für J.
Und da können wir natürlich gleich gucken, ob wir das da einfügen können.
Wir machen aber vorher noch was anderes. Wir überlegen uns,
wo ist denn eine alternative Position für das Element,
was momentan den Platz blockiert, wo wir sind.
Das ist T von
TV und Jesus, das steht ja an der Position J. Und dann rufen wir die H2-Funktion,
die zweite Hash-Funktion für das TV und Jdov, addieren das da drauf,
rechnen wieder Modul UM und haben eine alternative Position.
Und jetzt sagen wir, okay, wenn,
wenn das nächste Feld frei ist, also J. Next,
das nächste, was wir angucken,
wenn das entweder ein Neil oder Deleted ist,
dann können wir da ja was einfügen.
Dann gehen wir dahin, dann nehmen wir das.
Wenn es alternativ, schauen wir uns an,
was mit dem J alt ist.
Wenn das nicht frei ist, dann nehmen wir auch
diese Position
Und anderenfalls vertauschen wir die, dann sagen wir,
okay, wir schreiben den Schlüssel von TJ,
da schreiben wir jetzt das K rein und das K schreiben wir ins TJ,
wir vertauschen die beiden Elemente und dann machen wir mit der alternativen Sequenz weiter.
Und damit machen wir genau diesen Sprungwechsel in dem Augenblick,
wo wir für das alternative Element eine Position finden.
Und in der nächsten Iteration würden wir das einsortieren können.
Ja, und der Rest ist wieder gleich,
wenn wir hier durch sind, dann ist T gleich M.
Und dann wissen wir, dass die Hashtagle voll waren,
haben wir gar keinen Platz gefunden.
Und anderenfalls können wir an die Position TJ
K hinschreiben. Okay, so funktioniert der Algorithmus,
der ist wunderschön, der ist korrekt, der funktioniert,
der hat nur einen Haken, es ist nicht Brands Algorithmus.
Und sowohl in diesem Lehrbuch, was sehr schade ist,
aber was noch viel schlimmer ist, auch in Wikipedia,
wird genau dieser Algorithmus mit Beispielen und allen drum und dran
als Brands Algorithmus verkauft.
Das Problem dabei ist, ich könnte sagen,
ist ja egal, ist ja auch ein toller Algorithmus.
Ja, aber man verbindet das immer mit der Analyse,
die der Herr Brand gemacht hat und die stimmt für
diesen Algorithmus nicht
So, da muss man also woanders nachgucken.
Man kann dann das Original-Paper lesen. Also nur,
das kann man machen, das ist ein bisschen unangenehm,
weil das da schon ein bisschen älter ist.
Und da steht nicht so ein schöner Pseudo-Code drin,
sondern das ist einfach nur so voll klausuliert im Text,
wie man das machen muss.
Wenn man dann reinguckt, sieht man aber tatsächlich den Fehler.
Und ich weiß nicht, wer von Ihnen diese Bücher von
dem Herrn Knus kennt. Kennen Sie alle Herrn Knus,
nicht persönlich, aber zumindest das, was er gemacht hat.
Das ist ein sehr bekannter Informatiker. Der hat also
nicht nur Tech gemacht, sondern der hat auch sehr,
sehr viel Resultate in der Informatik gemacht.
Und der hat aber auch insbesondere diese Buchreihe hier geschrieben,
Art of Computer-Programming
Diese Buchserie, kennt ihr einen? Hatte einer von ihnen
schon mal reingeguckt in diese Bücher? Sie haben da schon
mal reingeguckt, sie auch. Sie haben sie zu Hause.
Ja, ja, es gibt solche, die haben die.
Also ich habe da auch mal reingeguckt, also die Bücher,
die nimmt man in der Lehre sehr selten,
weil der Herr Knus auch ein relativ eigenwilliger Mensch ist.
Und der fängt als erstes mal an und erzählt ihnen,
wie er denn der Meinung ist, wie Programmiersprachen sein wird,
hat dann seine eigene Sprache.
Und es ist alles sehr, sehr schwierig zu erfassen.
Es ist nicht so ganz so einfach.
Na gut, aber dafür macht er es auch richtig.
Also Herr Knus beschreibt den Brand-Algorithmus und ich habe versucht,
es hier nochmal zusammenzufassen,
wie der Algorithmus funktioniert
Der eigentliche Fehler in dieser Beschreibung ist, ist,
dass man hier immer nur für den letzten Sondierungsschritt zwei Alternativen probiert.
Und das ist falsch. Das macht der Brandalgorithmus nicht.
Der Brandalgorithmus geht nochmals zurück. Also wenn beim Tätenschritt keine Platz gefunden wurde,
geht er die ganze Folge zurück und sucht für alle Punkte,
für alle Werte auf dem Weg.
Die nächste Sondierungsmöglichkeit nochmal. Das muss man machen, damit
die Analyse nachher stimmt. Das andere wäre auch richtig,
aber dann kriegen sie nicht die Performance, die der Algorithmus hat.
Okay, so, das sieht also so aus.
Wir haben also die Sondierungsfolge, wir betrachten die bis zum Zeitpunkt
Also hier wäre P.0, wir versuchen also unseren Schlüssel K einzufügen,
das wäre der erste Versuch gewesen, der zweite Versuch,
der dritte, der vierte, der fünfte.
Sie sehen, das ist hier die Sprungweite von der zweiten Hashtag-Funktion,
die ist immer gleich, um das einzufügen.
So, wir betrachten jetzt den Zeitpunkt T. Das bedeutet,
wir haben also die Sondierungsfolge bis hier gefolgt und diese Felder
waren alle belegt. Was wir jetzt schauen ist,
ob entlang dieser Folge ein Element ist,
was wir mit einem Sondierungsschritt
weniger einsortieren könnten
So, das heißt, wir versuchen also die mit t-i
minus eins-facher Sondierung zu platzieren. Also für den gönnen wir uns
einen weiteren Platz, für den zwei weitere und so weiter.
So, für dieses Element X, das läuft auf einer anderen Sequenz.
Das hat ja eine andere Sprungweite. Das kommt von irgendeiner Position hier,
ist danach hier, hier ist es eingefügt worden und hier wäre die nächstmögliche Position für dieses Element.
Ja, so, was man also macht, ist,
man muss quasi alle Elemente nochmal anschauen und gucken,
ob man für die mit einem weiteren Sondierungsschritt,
also nicht einen, sondern den muss man ja dann ausrechnen,
den t-i minus ersten Sondierungsschritt einen Platz findet.
Okay, wenn man das so macht, dann sieht man,
dass man die durchschnittliche Länge in der Bilanz von allen Sondierungsfolgen,
die man hat, um eins verkürzen kann. Ja,
also das ist das, was Brand zeigt,
der sagt dann, okay, also die Position,
also man schreibt dann das X an diese Position hinein und dadurch verlängert sich die Sequenz für das X,
das wird ein bisschen länger,
es wird um t-i-i-1 Schritt länger, aber das K,
das können wir dann an diese Stelle hineinschreiben,
wo das vorher stand,
dieses Element,
und dadurch verkürzt sich die Sequenz um t-i
Und wir gewinnen genau eins. Also einen Schritt summiert über
alle Sondierungssequenzen, die wir haben. Okay,
und diese eins reicht aus, um beweisen zu können,
dass egal wie voll die Hash-Tabelle ist,
wir unsere Suchelemente im Mittel, im Mittel ist ein Erwartungswert,
im Mittel in 2,5 Schritte finden.
Ja, und das ist nicht mehr abhängig vom Belegungsfaktor.
Das absolut faszinierend ist, weil man würde doch erwarten,
wie voll er diese Tabelle ist,
umso schwieriger ist es dann eine Sondierungsfolge zu haben,
wo wir das Element tatsächlich finden
Das ist ein ganz erstaunliches Ergebnis, dass das funktioniert.
Also 2,5 kommt da raus. Das ist eine informatische Naturkonstante,
wenn sie so wollen. So, das kommt bei Brands
Algorithmus raus. Hier steht er auch einmal aufgeschrieben, wie
er eigentlich sein müsste. Das ist jetzt so grün.
Ich hoffe, können Sie das lesen? Und ich glaube,
wir müssen noch nicht Teile für Zeile da durchgehen.
Der ist ja schon ein bisschen exotisch. Sie haben jetzt
auch ein kleines Problem, weil ich sage Ihnen ja immer,
Sie müssen das alles im Kormen nachlesen, um es richtig zu verstehen.
Das kommt im Kormen gar nicht vor. Aber weil es ja
auch bei Wikipedia falsch ist, wollte ich sie mal einmal richtig zeigen.
Also so muss es in etwa aussehen und sie sehen,
sie haben in dieser Wildschleife, die die Sondierung eigentlich für das Objekt macht,
eine weitere Vorschleife.
Und das ist genau der Fehler in der Beschreibung,
die sie im Internet finden. Hier ist nochmal eine Vorschleife,
die für alle Elemente sozusagen prüft, ob mit der nächsten Sondierung.
Das ist wieder genauso. Da rechnet man dieses coonext aus
und kann man dann gucken, ob das eine Alternative ist und dann vertauscht man die.
Also wie gesagt, wir müssen da nicht Teile für Teile durchgehen.
Und dann hat man genau dieses Verhalten. Und hier sieht
man auch etwas, was Brand tatsächlich ganz gut beschreibt.
Man muss sich natürlich fragen, wir sagen hier, wow,
2,5 Sondierungsschritte bei der Suche. Aber die Frage ist natürlich,
was passiert denn beim Einfügen? Also wie immer,
alles im Leben hat einen Preis und der Preis,
den man hier zahlt, ist natürlich beim Einfügen.
Wir haben jetzt also diese zwei geschachtelten Schleifen und wir haben
hier so ein quadratisches Laufzeitverhalten im Belegungsfaktor.
Da ist irgendwie so was Alpha-Quadratähnliches, was da rauskommt.
Das kann man auch analysieren, steht doch alles da drin.
Das heißt, einfügen mit Brands Algorithmus ist teuer.
Auch das kann man natürlich erwarten,
weil der Algorithmus findet eine so tolle Geschichte,
die Objekte zu platzieren, dass wir schnell suchen können.
Das kostet halt, wir brauchen halt Zeit, um diese Folge zu finden.
Okay, gut, das ist Brands Insert-Operation. Und das
ist das Ende zum Thema Hashing. Was haben wir gelernt? Wir haben gesehen,
erstens, wenn wir in der Lage sind,
Schlüssel in Indizes umzurechnen, und das machen sogenannte Hash-Funktionen,
dann können wir so ein Wörterbuch,
wo wir Objekte einfach nur speichern,
löschen und suchen möchten,
mit nahezu konstanten durchschnittlichen Laufzeiten realisieren
Sehr viel über Worst-Case-Laufzeiten gesehen. Wir haben fast immer über
den durchschnittlichen Fall argumentiert. Das ist nochmal ganz wichtig.
Also Hashing kann im Worst-Case unangenehm sein,
aber im Schnitt haben wir halt sehr kurze Zeiten.
Die Laufzeiten, auch wieder Average Case, also im mittleren Fall,
hängen in der Regel vom Belegungsfaktor ab.
Ja, also je voller die Tabelle, umso länger dauert es.
Muss man einen Platz finden, müssen wir gucken,
um das Objekt dann wiederzufinden. Kollisionen sind generell nicht vermeidbar.
Hash-Tabellen haben eigentlich immer Kollisionen eigentlich, muss ich sagen.
Auch dazu werde ich noch was sagen gleich.
Und deswegen brauchen wir Kollisionsvermeidungsstrategien
Im Worst Case, wenn wir wirklich den Worst Case analysieren,
haben wir in der Regel Laufzeit, Täter von N.
Also dann haben wir die Situation, alles liegt in der gleichen Liste.
Alles hängt quasi bezüglich der Verkettung hintereinander und man muss es
sich alles anschauen. Aber das ist, wie gesagt,
der Worst Case. Dann haben wir gesehen, okay,
wir haben die zwei Kollisionsstrategien, Listen und wir haben offene Adressierung.
Und wir haben gesehen, wenn wir heute Daten häufig löschen,
dann können wir bei dieser offenen Adressierung,
die eigentlich ein bisschen eleganter ist, speichereffizienter ist,
nicht so gut damit umgehen und haben immer diese gelöschten Felder.
Ist also nicht so günstig. Das heißt,
wenn man viel löscht, ist eigentlich eine Verkettung besser
Offene Adressierung, Speichereffizienter, wir brauchen keine Pointer,
wir können genau den Speicher platzieren,
wir müssen und allokieren, den wir brauchen.
Die Operation, die Lead ist Laufzeitschädlich für die Suche.
Das heißt, wenn wir häufig die Lead machen,
dauert die Suche im Schnitt länger, als sie müsste.
Haben wir darüber gesprochen. Offene Adressierung sollen, sollte angewendet werden,
immer dann, wenn wir also wenig Lead-Operationen haben und mehr suchen.
Wenn man es macht, sollte man auf alle Fälle
doppeltes Hashing verwenden, also zwei Hash-Funktionen verwenden.
Und dann haben wir Brand gesehen,
Brand ist dann gut, wenn sie,
ja, mehr oder weniger wenig einfügen löschen und viel suchen
Weil die Suche hocheffizient ist. Wenn sie viel löschen und immer wieder einfügen,
dann müssen sie das bezahlen mit einer teuren Insert-Operation.
Die ist ja ein bisschen langsamer. Aber wenn sie so ein Szenario haben,
dass sie, was weiß ich,
ein Prozent der Zugriffe auf die Datenstruktur sind,
Einfüge-Operationen und 99 Prozent sind Suchoperationen, dann würde man sagen,
okay, dann lohnt sich das.
Ja, dann lohnt es sich ein bisschen mehr Zeit für das Einfügen zu opfern.
Und es geht mit einer konstanten Anzahl von Sondierungsschritten.
Die 2,5 ist gar nicht das Spannende.
Da könnt ihr auch 3,5 sein. Die Tatsache ist,
es ist konstant,
unabhängig vom Belegungsfaktor
Wie so immer ist das auch hier nicht das Ende der Geschichte.
Ja, auch zu Hashing könnte man jetzt wieder viele,
viele Vorlesungen was erzählen. Wenn wir eine,
was auch ganz interessant ist, ist,
wenn wir eine statische Datenmenge haben, wenn sie sagen,
zum Beispiel hier ist der Amazon-Buchkatalog und den ändere ich jetzt die nächsten Monate nicht.
Ja, also eine statische Menge. Dann können sie natürlich
noch was rausholen, weil dann können sie diese Datenmenge analysieren
und für die Datenmenge einfach die optimale Hash-Funktion suchen.
Dann investieren sie richtig Rechenzeit und sagen,
wenn ich diese Hash-Funktion nehme,
dann teilt dir die Daten super auf und ich habe auch
ganz wenig Kollision
Diese Techniken nennt man perfektes Hashing, weil man sucht eben
diese optimale Hash-Funktion. Und da kann man sogar zeigen,
dass wir eine Worst-Case-Laufzeit von UFON 1 haben.
Sogar für Insight und Search. Aber wie gesagt,
das gilt für statische Datenmengen. Ja,
das machen wir nicht hier in der Vorlesung,
weil wir haben ja noch so viele andere interessante Geschichten,
die wir uns anschauen können. So,
das ist das Ende des Datenstrukturkapitels und sie haben die Gelegenheit,
schnell noch was zu fragen und in der Zwischenzeit suche ich
meine nächste Datei,
wenn sie noch was fragen möchten
So. Nicht, dass Sie glauben, ich wäre ein Raubkopierbenutzer.
Ich habe eine Lizenz. Microsoft glaubt das nur nicht,
dass ich eine Lizenz habe.
So. Okay. Graf-Algorithmen. Das ist unser neues Kapitel.
Damit werden wir uns jetzt beschäftigen und das werden wir
auch garantiert
bis nach Weihnachten tun
Und Graf-Algorithmen haben sie, ja,
vielleicht an der einen oder anderen Stelle schon gesehen.
Wir werden erst mal gleich sehen, was Grafen eigentlich sind.
Und dann werden wir uns anschauen, was wir mit Grafen
tun können, welche Probleme wir damit modellieren können.
Und dann werden wir anfangen, uns Algorithmen anzuschauen, die auf Grafen arbeiten.
Das werden wir jetzt tun. So, wir beginnen mit einer,
naja, relativ mathematischen Einstieg in dieses Thema,
was Grafen eigentlich sind, viel wichtiger als die Formel,
sind eigentlich diese Bilderchen hier.
Grafen sind folgende Konstrukte, wir haben Objekte,
die haben hier bei mir Nummern,
Und wir haben Beziehungen zwischen diesen Objekten.
Und die sind dargestellt durch solche Kanten. Und dieses System
aus Objekten und einfachen Beziehungen zwischen Paaren von solchen Objekten werden Grafen genannt.
Im Kontext von Grafen sind diese Objekte Knoten und diese Beziehungen zwischen denen
sind Kanten. Ja, das ist alles eigentlich schon.
Da gibt es jetzt natürlich viele Varianten von Grafen.
Zum Beispiel gibt es ungerichtete Grafen, wo diese Beziehungen
quasi in beide Richtungen symmetrisch ist.
Deswegen malen wir auch nur so einen Strich dazwischen.
Und es gibt gerichtete Grafen, da ist diese Beziehung
dargestellt durch einen Pfeil. Also die Beziehung geht von dem
einen Knoten 1 zu dem anderen Knoten
Okay, so, also etwas mathematischer gesprochen, haben wir also immer eine Menge von Knoten.
Das waren diese Objekte hier und eine Menge von Kanten. Wir werden im Folgenden,
und das ist Standards, auch durch alle Lehrbücher hindurch,
die Anzahl der Knoten immer mit N bezeichnen und die Anzahl
der Kanten immer mit M. Groß-N und Groß-M. Das sind die Anzahl,
das ist die Größe, das wird für uns natürlich wichtig sein,
wir werden Algorithmen entwickeln und die hängen natürlich in der Laufzeit von der Größe dieses Grafen ab,
also von N und M.
So, wenn der Graf ungerichtet ist,
dann sind die Kanten eine Teilmenge der zwei elementigen Mengen von
X und Y und X und Y sind Knoten.
Ja, und X ist dann ungleich Y,
wir haben keine Kante, die von einem Knoten zu sich selber geht.
So, hier wie so ein Eselsohr, so wie das hier.
Das gibt es allerdings nur bei gerichteten Grafen.
Bei gerichteten Grafen kann es manchmal Schleifen geben.
Das heißt, das ist Schleif, ein bisschen Schleifen.
Hier gibt es keine Schleifen, beim gerichteten Grafen gibt es
schränk genommen auch Schleifen,
Wir werden, das ist eine gute Frage.
Ich glaube auch, dass da die Literatur nicht eindeutig ist.
Also es gibt viele Beispiele, wo man redet von einem Algorithmus
mit so und so viel Kanten und so und so viel Schleifen.
Für uns ist streng genommen eine Schleife eine zusätzliche Kante,
die von einem Knoten zu sich selber geht.
Wir werden es als eine zusätzliche Kante bezeichnen. Okay,
aber ich kann Ihnen auch sagen, die werden bei uns keine große Rolle spielen.
Wir werden im folgenden eigentlich diese Schleifen nicht groß betrachten.
Okay, so, das ist erstmal ein Graf. Dann gibt es ein Multigraf,
ein Multigraf ist ein Graf, wo so eine Kante,
wo es multiple Kanten geben kann zwischen zwei Knoten,
Da würde also von 1 nach 2,
würde ich nur eine Kante laufen,
sondern noch eine und noch eine, noch eine.
Dann reden wir von einem Multigraph. Und ein Hypergraph,
ein Hypergraph ist ein Graph,
wo die Kanten eine Teilmenge der Potenzmenge der Knoten ist.
Das bedeutet nichts weiter, als das eine Kante,
nicht nur zwei Knoten miteinander verbinden kann, sondern beliebig viele.
Also der kann dann drei Knoten miteinander verbinden dehnen dehnen und irgendwie dehnen.
Das ist dann so, oder fünf oder zehn oder so.
Dann reden wir von einem Hypergraphen.
Wir werden in dieser Vorlesung weder multi noch hypergraphen
algorithmisch untersuchen
Man auch alles später machen. Wir werden nur mit einfachen Grafen arbeiten,
aber sie sollten diese Begriffe kennen, weil,
wenn man Probleme modelliert, dann hat man eben manchmal dieses Szenario.
Das beste Beispiel zum Beispiel für so einen Hypergraf ist,
wenn sie, also ja, größten als alle,
ja, Informatiker, Kerninformatiker, wenn sie Elektronik beschreiben,
ne, Leiterplatten, kennen sie sowas, ne,
also da, wo man Chips drauf lötet, da,
wenn sie dann beschreiben,
welche Stellen miteinander verbunden sind in so einem Leiternetz,
dann haben sie keinen Graf,
sondern einen Hypergraf,
weil solche Leitungen sich ja verzweigen können und
mehrere Punkte miteinander verbinden können
Das ist so ein Beispiel, wo man Hypergraphen hat.
Multigraphen hat man zum Beispiel bei so, wenn sie Wegeprobleme haben,
wenn sie zum Beispiel ihr Navigationssystem selber programmieren würden,
dann müssen sie irgendwie das Kartennetz modellieren,
das macht man durch Graphen.
Und da hätte man vielleicht zwei alternative Straßen,
die von Ort 1 zu Ort 2 gehen.
Und dann würden sie hier auch zwei Kanten benötigen.
Hätte man ein Multigraph. Also Anwendung gibt es da
für viele. Es gibt natürlich auch viele Algorithmen dafür,
werden wir im Folgenden aber erstmal nicht betrachten.
Wir machen nur die einfachen Dinge
So, jetzt brauchen wir noch so ein bisschen Nomenklatur,
weil wir müssen die Sprache lernen, die diese Leute benutzen, die mit Graphen arbeiten.
Die wichtigsten Begriffe sind Adjazenz und Inzidenz. Also wir sagen,
ein Knoten weh ist Adjazenz und Knoten V,
wenn die beiden durch eine Kante verbunden sind.
Also sind also Nachbarn sozusagen. Und wenn wir eine Kante betrachten,
dann sagen wir, die ist inzident mit einem Knoten V,
genau dann werden diese Kante an diesen Knoten anstößt sozusagen,
wenn die dazugehört, wenn die damit verbunden wird.
Bei einem gerichteten Graphen gibt es jetzt ja zwei Möglichkeiten,
entweder der Knoten kann der sein, wo die losgeht,
so wie bei der Kante der Knoten 1. Oder es kann der Knoten sein,
wo die Kante endet und dann redet man von Inzident von V.
Also man geht von V los oder man redet von Inzident nach V.
Also man geht zu weh hin. Ja, das ist
immer, wenn die gerichtet sind. Okay, so,
jetzt gucken wir uns also diese Grafen an und das erste,
was wir mal feststellen, ist, okay,
Knoten haben offensichtlich eine gewisse Anzahl von Kanten,
die da dran stoßen und das ist unser Knotengrad.
Ja, also dieser Knoten hat zum Beispiel den Grad 2,
der hat auch den Grad 2,
dieser hätte den Grad
Das wäre der Grad. Also die Anzahl der Kanten mit
der Eigenschaft, dass der Knoten zu der Kante gehört.
Bei gerichteten Graffen müssen wir hier wieder unterscheiden.
Da gibt es zwei Fälle. Also wir können sehen,
wie viele Kanten gehen da rein und wie viele Kanten gehen da raus. Also dieser Knoten 2,
der hätte dann einen eingehenden Knotengrad oder ein Indigree in Neudeutsch von 2,
1, 2 und ein Outdegree von 3, 1,
2, 3. Und dann sehen Sie schon,
es wird schon wieder schwierig mit diesen blöden Schleifen,
weil bei so einer Schleife 10 wie die Kante, sowohl beim Indigree als auch beim Outdegree.
Okay, also hier steht es indigree,
ist die Anzahl der Kanten mit der Eigenschaft, das X,
kannte von einem Knoten X zu V geht.
Also das geht in den Knoten V rein und der Autegree ist die Eigenschaft,
die Anzahl der Kanten, wo die Kante von V herausgeht,
zu irgendeinem anderen Knoten X und der Grad ist dann die Summe von den beiden.
So, dann können wir Pfade bilden.
Wir können also jetzt in dem Graf hier so langsam,
können von 1 über 2 zu 5 gehen zum Beispiel.
Pfade sind natürlich super wichtig in Grafen und ja,
deswegen sind die definiert. Wir sagen also im Pfad,
er hat eine spezielle Länge,
das ist dann also eine Knotenfolge, also V0,
V1 vor 2 bis VK und das ist nur dann ein Pfad,
wenn jeweils zwei benachbarte Knoten hier
auch mit einer Kante verbunden sind
Also VI minus 1, VI ist dann ein Element eh
in der Knotenmenge in der erkannten Menge enthalten.
So ein Pfad, der heißt dann einfach,
wenn kein Knoten zweimal besucht wird.
Wir könnten ja auch so im Kreis laufen und dann so
einen Knoten mehrmals besuchen, dann ist er nicht mehr einfach.
Sonst ist er einfach. Und ein Teilpfad ist noch nichts
weiter als ein Stück daraus. Also eine Teilmenge daraus.
Diese Teigfahrt geht dann von VI bis VJ.
Dann haben wir Kreise oder Zyklen und Kreise und Zyklen
haben die Eigenschaft, dass der erste Knoten dem
letzten Knoten entspricht
Ja. Ich möchte Ihnen empfehlen,
den Begriff Zyklus zu verwenden und nicht den Begriff Kreis,
Sie finden den Begriff Kreis manchmal in der deutschsprachigen Literatur.
Im Englischen ist das sehr eindeutig. Da heißt das nämlich Cycle.
Und es heißt nicht Circle. Und ich weiß nicht,
wie es gekommen ist, dass in manchen deutschsprachigen Büchern von
Kreisen die Rede ist. Ich finde Kreis eigentlich ungünstig,
weil es hat nichts mit einem geometrischen Kreis zu tun,
sondern nur damit, dass wir Objekte in einer Folge besuchen,
sodass wir an dem Ursprungspunkt wieder ankommen.
Das ist eben
ein Zyklus
Okay, aber Kreise finden sie auch. Und wenn so
ein Graf keine Zyklen hat oder keine Kreise hat, dann ist der arzyklisch.
Okay. So, Erreichbarkeit. Erreichbarkeit ist ein ganz wichtiges
Kriterium. Erreichbarkeit bedeutet, es gibt irgendwie einen Pfad,
um von einem Punkt zu einem anderen zu kommen.
Wir sagen, V ist erreichbar von W.
Also wir gehen jetzt bei W los und wollen V erreichen.
Genau dann, wenn es ein Pfad von W nach V gibt.
Bei ungerichteten Grafen ist es ja so, naja,
da können wir den Vatern auch in der anderen Richtung durchlaufen. Dann,
wenn also V erreichbar von W ist,
ist auch weh erreichbar von V. Deswegen können wir ungerichteten Grafen direkt sagen,
ein ungerichteter Graf heißt verbunden, im Englischen connected,
genau dann, wenn für alle VW gilt,
also für alle Kanten gilt, es existiert ein Pfad von V nach W.
Und weil das ungerichtet ist,
gibt es dann auch ein Pfad von W nach V. Verbunden heißt also,
egal von welchem Knoten sie starten,
sie finden einen Pfad zu jedem anderen Knoten in dem Graf, dann ist es verbunden.
Okay, beim gerichteten Graf ist es ein bisschen schwieriger,
weil da müssen wir unterscheiden, da kann es ja sein,
dass wir von V zwar Wege lang,
aber von wenig mehr zurück zu V, weil dazwischen sind ja,
da sind ja gerichtete Kanten drin und wenn so eine gerichtete Kante geht ja nur in Einrichtung,
aber es kann sein,
dass du in die eine Richtung kommst und in die anderen,
in die andere nicht.
Deswegen verwendet man den Begriff der Verbundenheit hier eigentlich nicht,
also er wird wirklich möglichst gar nicht verwendet,
sondern im gerichteten Fall redet man immer von starker Verbundenheit,
also strongly connected. Und zwar sagt man,
dass der gerichtete Graf stark verbunden ist, genau dann,
wenn für alle Knoten VW aus der Knotenmenge gilt,
es existiert ein Pfad von V nach W und und
von W
Ich habe das bewusst hier so stehen lassen mit dem Un von W nach V.
In der Regel zeigt immer einer auf und sagt,
ja, das musste aber gar nicht stehen.
Ja, das muss da auch nicht stehen,
weil es ist ja für alle VW aus V.
Und dann können wir die auch umtauschen. Also die Definition ist auch ausreichend,
wenn wir einfach sagen,
es ist existiert ein Fahrt von V nach W. Ich schreibe das aber hierhinter,
um es einfach etwas deutlicher zu machen,
dass man einen Weg in die eine Richtung finden muss und einen Weg in die andere Richtung wieder zurück.
Ja? Okay. So, dann haben wir Teilgrafen.
Wir können immer auch einen Teil eines Grafen betrachten.
Das ist ein sogenannter Subgraf. Ein Subgraf hat die Eigenschaft
. Wir nehmen einen Teil der Knotenmenge und wir nehmen einen
Teil der kannten eine Menge heraus. Natürlich dürfen wir jetzt
nur Kanten nehmen, die an diesen Knoten dran sind,
die machen ja sonst keinen Sinn. Häufig ist es so,
dass wir nur die Knoten beschreiben, wir sagen also,
diese Knoten nehmen wir und davon betrachten wir den Subgraf und dann stellt man sich die Frage,
ja, was sind jetzt die Kanten?
Naja, die Kanten sind eben, alle kannten die zwischen diesen Knoten verlaufen.
Und das ist dann der sogenannte induzierte Sograf. Also,
wenn wir eine Konotenmenge V-Strich haben und E-Strich sind einfach alle Kanten der ursprünglichen Kantenmenge,
wo U und V in dieser Menge V-Strich ist,
dann ist es
ein induzierter Sograf
Okay, so, Zusammenhang. Wenn wir jetzt so einen
Graf haben, dann wissen wir, wir wissen jetzt,
was es bedeutet, dass er zusammenhängend ist.
So ein Graf ist aber nicht notwendigerweise zusammenhängend.
Stellen wir uns einfach mal vor, sie schreiben ein Navigationsgerät
für Google Maps, das heißt für die ganze Welt.
Und da haben sie als Knoten alle Städte und als kannten
alle Straßen. Dieser Graf ist dann nichts zusammenhängend,
weil sie haben ja Kontinente oder sagen wir besser Inseln irgendwo,
die haben einen für sich ein Straßennetz und innerhalb dieser,
wenn sie auf der Insel sind,
dann können sie jeden Punkt von jedem anderen Punkt erreichen auf der Insel.
Aber sie kommen nicht, zumindest nicht mit dem Auto,
von dieser Insel und ohne Fähre, von dieser Insel zur Nachbarinsel.
Das heißt, dieser Graf, der in Google Maps
quasi alle Straßen darstellt, alle Orte und alle Straßen,
ist streng genommen erstmal nicht zusammenhängend.
Trotzdem hat er Bereiche, die in sich zusammenhängen sind.
Alle Orte mit allen Straßen in Deutschland sind offensichtlich zusammenhängend.
Sie können mit dem Auto von jedem Ort zu jedem anderen
fahren.
Sie könnten das Experiment übrigens mal machen,
wenn sie es noch nicht getan haben.
Es ist ja sehr lustig und mal bei Google Maps,
da können Sie ja mit From Two Routen ausrechnen lassen,
tippen Sie mal Hamburg, Hamburg, New York ein.
Und gucken Sie, wie Google mit der Situation umgeht,
dass dieses Straßennetzwerk eigentlich nicht zusammenhängt ist.
Google schlägt Schwimmen vor. Okay. Also,
6400 Kilometer, die Entfernung geben Sie dann an.
Okay, also, wir zerlegen den Grafen jetzt in
zusammenhängende Bereiche
Das sind sogenannte Zusammenhangskomponenten oder Connected Components. Das ist einfach
der maximal zusammenhängende Subkraft eines umgerichteten Grafen. Das ist so eine Komponente.
Und wenn sie sich das mit strassen kann und den Inseln vorstellen,
dann sind eben, wenn sie jetzt eine Insel haben,
alle Städte, die auf der Insel sind und alle Straßen,
ist dann eine Zusammenhangskomponente.
Das Maximum, was sie noch dazunehmen können, was sie erreichen können.
Und den Begriff gibt es dann natürlich auch für gerichtete Grafen.
Das ist dann eine starke Zusammenhangskomponente. Es ist strongly
connected componed. Also der analoge Brichtbegriff für gerichtete Grafen.
Hier muss man nur ein bisschen aufpassen,
weil man das optisch nicht mehr so klar sieht.
Also da laufen ja durchaus schon Kanten dann zwischen diesen Zusammenhangskomponenten,
die sind nur alle so gerichtet, dass man nicht wieder zurückkommt.
Die sind so wie Einbahnstraßen, die so konstruiert sind,
dass man zwar von der einen Inselhälfte in die andere fahren kann,
aber man kann nicht wieder zurückfahren.
Das gibt es natürlich nicht. Aber so kann man sich
das vorstellen. Das ist der starke Zusammenhang. Okay,
und letztendlich noch der Begriff der Isomorphie. Wir sagen,
wir müssen uns noch fragen, wann sind Grafen eigentlich gleich?
Wann ist es eigentlich das Gleiche? Und da würde man sagen,
ja, wenn die hier so gleich aussehen,
aber das ist ein schlechter Gleichheitsbegriff, weil die Art und Weise,
wie wir so einen Grafen zeichnen,
ist eigentlich gar nicht so wichtig.
Den können wir oft tausende von verschiedenen Arten
Zeichnen. Das heißt,
wir sagen, sein Graf ist Isomorph, also gleich,
wenn sie so wollen, wenn es eine Abbildung gibt,
die die Knotenmenge von dem ersten Graffen auf die Knotenmenge des zweiten Grafen abbildet.
Und das ist eine bioaktive Abbildung. Das bedeutet,
jedem Knoten der ersten Menge wird genau ein Knoten der zweiten Menge zugeordnet.
Und dann muss gelten, dass immer dann,
wenn in dem ersten Grafen eine Kante war,
in dem zweiten Grafen auch eine Kante sein muss.
Dann sind die Isomorph. Isomorphie zu erkennen ist überhaupt nicht einfach.
Das hört sich so an, ja, irgendwas zu gucken,
ob das gleich ist, hört sich erstmal einfach an.
Es würde die Grafen eine relativ schwierige Aufgabe.
Also hier haben wir einen Grafen mit sechs Knoten.
Hier haben wir auch einen Grafen mit sechs Knoten.
Und jetzt kann man sich fragen, sind die jetzt Isomorph oder nicht?
Und sie sehen selbst, wenn sie da drauf gucken,
bei so einem kleinen Beispiel müssen sie schon ein bisschen überlegen,
weil sie wissen nicht genau, welcher Knoten hier könnte denn zu welchem gehören.
Und dann müssen sie alle diese Kanten überprüfen, ob die auch alle existieren.
Es gibt ein paar Kriterien, die man natürlich immer benutzen kann,
um zu erkennen, ob was Isomorph ist.
Aber das sind nur so Ausschlusskriterien. Also was man immer macht,
ist, als allererstes gucken wir mal,
die Anzahl der Knoten gleich, sonst geht es natürlich gar nicht.
Also die Anzahl der Knoten muss gleich sein, Anzahl der Kanten muss natürlich auch gleich sein.
Und was immer noch ganz gut ist, ist,
wenn man auf Gleichheit prüft, man überprüft die Knoten gerade,
weil die Knoten, die man aufeinander zuordnet, müssen ja den gleichen Grad haben.
Und wenn man dann zum Beispiel in einem Grafen wie hier einen Knoten hätte mit ein,
zwei, drei,
vier Kanten und dann guckt man sich den Grafen hier an,
da sieht man, hier gibt es überhaupt keinen Knoten,
der wog vier Kanten, zu dem vier Kanten Inzidenz sind,
dann weiß man, okay, also die beiden können gar nicht isomorph sein.
Die beiden sind isomorph und die Zuordnung,
die man dafür braucht, ist,
dass man den Knoten eins auf U legt und den
zweiten Knoten zwei auf Fow liegt und so weiter.
Und dann ist das isomorph
Okay, so. Eigentlich kennen sie schon Grafen,
also zumindest einen speziellen Graf, kennen sie schon.
Das ist ein Baum. Wir haben Suchbäume rauf und runter
betrachtet im letzten Kapitel. Bäume sind Grafen. Wir haben
diese Bäume gesehen. Und wenn wir sie allgemein beschreiben,
dann ist ein Baum definiert als ein ungerichteter, zusammenhängender,
Graf ohne Zyklen.
Da steht noch Kreise, das muss ich mal irgendwie ändern.
Kreise geht auch, aber Zyklen ist besser.
Also ohne Zyklen. Also er muss zusammenhängend sein,
ungerichtet,
keine Zyklen
Und diese drei Kriterien selten erstmal für einen Graf.
Für einen Baum, Entschuldigung, für einen Baum.
Beim Baum unterscheiden wir zwei Varianten. Es gibt den Gewurzelten und den Ungewurzelten,
das hört sich im Deutschen immer sehr blöde an.
Also im Englischen ist das der Routed Tree,
also der mit einer Wurzel oder der Unrouted Tree ohne Wurzel.
Der einzige Unterschied ist, dass sie einen Knoten auszeichnen und sagen,
du bist Wurzel. Das ist der einzige Unterschied. Jeder Baum,
also hier habe ich ihn in meinem Baum gemalt,
der bewusst nicht so ist wie der, den sie schon so kennen,
also wie so ein Suchbaum, der hat jetzt keine ausgezeichnete Wurzel.
Sie können jetzt jedem Knoten sagen,
du bist jetzt die Wurzel und dann können sie den anders malen,
so wie sie es gewohnt sind,
von oben nach unten und dann sieht er so aus,
wie sie ihn kennen.
Okay, also das sind Bäume mit und ohne Wurzel
Wo Bäume sind, gibt es auch Wälder.
Ja, in der Informatik gibt es dann auch Wälder.
Ein Wald ist einfach eine Menge von Bäumen, was sonst.
Und das bedeutet, die einzige Eigenschaft, die man nicht mehr braucht,
ist der Zusammenhang. Wenn sie den Zusammenhang entfernen,
haben sie also eine Menge von, ja, Grafstücken.
Die sind alle, ist alles zyklenfrei und es sind alles
kleine Bäume, wenn sie so wollen.
Ja, jede Zusammenhangskomponent ist ein Baum und das ist zusammen ein Wald.
Ganz wichtig für uns wird noch ein sogenannter Deck sein.
Decks gibt es viel in der Anwendung, die muss man immer erkennen,
ob man einen sogenannten Deck hat, weil wir werden sehen,
dass für ganz viele Probleme wir die Laufzeiten enorm beschleunigen können,
wenn wir Decks haben.
Ein Deck, das ist ein Deck, ist eine Abkürzung für das englische Wort,
das eines Directed Ascyclic Graph, also eines gerichteten Azyklischen Graphen.
Das ist also ein gerichteter Graph, der keine Zyklen hat oder keine Kreise.
Hier ist ein Beispiel für so einen Deck. Sie sehen,
die haben alle Pfeilspitzen und sie sehen schon hier,
dass das so ein bisschen, erst so ein bisschen irritierend ist,
weil man denkt, ja hier ist ja so ein Zyklus,
aber hier ist kein Zyklus, weil sie natürlich die Richtung der Kanten berücksichtigen müssen.
Und sie finden hier keinen Zyklus. Sie finden keine Möglichkeit
von einem Punkt über eine Kantenfolge irgendwo hinzugehen und dann zu
dem Punkt wieder
Kann man hier ganz leicht dran sehen, dass das so ist,
weil alle Kanten verlaufen irgendwie von links nach rechts.
Und deswegen gibt es keine Zyklen. Das ist ein Deck. Okay,
das Maximum-Mankanten kriegen wir bei einem vollständigen Graf,
ein vollständiger Graf, ist hier, hier ist zum Beispiel
ein vollständiger Graf mit fünf Knoten, also fünf Knoten.
Jeder Knoten ist mit jedem verbunden, ist ein vollständiger Graf.
Dann gibt es einen BI-Partiten-Graf,
BI-Partite-Grafen kommen in der Praxis auch sehr viel vor,
BI-Partite-Grafen sind so,
dass die Knoten in zwei Mengen aufgeteilt werden,
U und W. Und dann gibt es Kanten
immer nur zwischen der Menge U und
Das ist ein bipatiter Graf. Dieses ist sogar ein vollständiger bipatiter Graf,
weil jeder Knoten mit jedem anderen der anderen Menge verbunden ist.
Okay, auch dafür gibt es wieder viele Beispiele, wo man sowas braucht.
Also bipatite Grafen braucht man bei allen Arten von Zuordnungsproblemen.
Wenn sie zum Beispiel in Zukunft eine Tanzschule leiten und sie müssen entscheiden,
wer mit wem zum Abschlussball gehen,
dann können sie das modellieren,
als einen bipatiten Grafen und dann können sie
ein sogenanntes Matching darauf berechnen
Dafür müssen sie aber noch die Algorithmik-Vorlesung hören, weil das machen wir hier
noch nicht. Okay, so, letzter Begriff noch,
Planaregraphen und dann haben sie es auch geschafft mit dieser ganzen Begriffswelt,
ein Planarergraph ist ein Graph, der sich ohne Kantenüberschneidungen zeichnen lässt.
Das ist eine ganz faszinierende Geschichte, weil plötzlich hat das
was mit Malen zu tun, wie man das in der Ebene zeichnet.
Und das passt eigentlich so gar nicht rein in dieses ganze Konzept,
weil bisher war alles eigentlich rein topologisch.
Und das ist plötzlich was, was mit Zeichnen zu tun hat.
Und hier ist so ein Beispiel, es hängt auch nicht davon ab,
wie sie den Graph zeichnen, sondern nur,
ob er prinzipiell ohne Überschneidung gezeichnet werden kann.
Dieser Graph zum Beispiel hier, Das ist fast der gleiche,
wie der hier oben, also der vollständige Graf mit fünf Knoten,
da habe ich nur eine Kante weggelassen.
Dieser hier, der ist Planar, der ist nur nicht Planar gezeichnet. Der ist Planar,
weil ich kann diese Kante genauso gut hier so romalen und diese Kante kann ich hier so romalen,
dann sieht das so aus wie hier und sie sehen,
wir haben eine Darstellung des Grafen, wo sich keine Kanten überschneiden.
Planarität von Grafen ist in der Praxis natürlich super wichtig,
weil wenn sie Bahnlinien planen, dann ist es gut,
wenn sie keine Überschneidungen haben.
Also vier Anwendungen sind so, dass sie sich für Planare Grafen interessieren.
Das Faszinierende an Planarengrafen ist, dass man lange nicht genau wusste,
was eigentlich die Eigenschaften sind, die Planarität ausmachen.
Und man hat herausgefunden, hat Kuratowski herausgefunden,
also ein russischer Mathematiker. Man hat ja herausgefunden,
dass es tatsächlich eine topologische Eigenschaft ist, die es ermöglicht,
ob das zeichenbar ist in 2D. Das war damals ein völlig erstaunliches Resultat,
weil man die ganze Zeit gedacht hat,
das hat irgendwas mit irgendwelchen komplizierten Zeichensachen zu tun.
Hat es aber nicht. Man kann es an der Topologie erkennen.
Und dieser vollständige Graf mit fünf Knoten ist nicht Planar,
also sie können diesen Grafen Nicht-Planner zeichnen, da können sie sich noch anstrengen,
so viel sie wollen, das geht nicht.
Und auch der vollständige Bipatite Graf mit sechs Knoten,
den sie hier sind, der ist auch nicht Planar.
Ich weiß nicht, kennen Sie das,
also zu meiner Zeit hat man in der Grundschule immer dann immer diese Aufgabe gekriegt,
da sind drei Häuser, A, B und C,
und da gibt es ein Wasserwerk und ein Elektrizitätswerk und ein Gaswerk.
Heute wäre es wahrscheinlich verschiedene Mobilfunkanbieter, keine Ahnung.
Und man versucht, diese Häuser zu verbinden,
ohne dass sich irgendwie die Leitung kreuzen.
Und dann hat man immer versucht, das irgendwie hinzukriegen,
hat gemalt und gemalt und gemalt
und das geht nicht
Dieser Graf hier ist nicht Planar, genau wie der.
Und das sind die kleinsten Grafen, die nicht Planar sind,
dass dieser Graf und dieser Graf,
weil der Vollständige auch mit fünf Knoten, dieser Graf hier,
der bi-patite vollständige Graf mit sechs Knoten, sind nicht Planar.
Und das Faszinierende, was der Herr Koratowski rausgefunden hat,
ist, dass das die einzigen beiden Topologien sind, die man überprüfen muss.
Sobald man festgestellt hat, dass keine Subgraf,
wobei man immer betrachten muss, es ist auch so Pfade betrachtet werden müssen,
die muss man dann so zusammenschrumpfen, zu kannten.
Solange man gezeichnet hat, dass keiner dieser Subgrafen diese Form hat,
also so ein fünf elementiger, vollständiger Graf oder so einer ist,
kann man sehen, dass das Planat zeichenbar ist.
Deswegen haben die heute auch einen anderen Namen, dieser hier,
das ist der K5 und das ist der K33. Wenn sie das mal irgendwo lesen,
wir können heute, es gibt heute Algorithmen,
mit denen können sie in Linearzeit,
also in Zeit der Größe der Grafen entscheiden,
ob der Graf Planar ist oder nicht,
aber das ist gehobener Sport,
das zu machen
So, jetzt überlegen wir uns, wie wir Grafen darstellen wollen im Computer.
Aber wir brauchen jetzt erstmal noch irgendwie eine Form, wie wir sie speichern.
Und da gibt es auch eine interessante Geschichte dazu,
weil Grafen sind nämlich eigentlich viel älter als die Informatik.
Das haben die Mathematiker schon lange, lange gemacht.
Ja, viele fangen bei Grafen auch immer an,
was zu erzählen von Euler und so.
Und wie der durch Königsberg gewandert ist und so weiter.
Das machen wir nicht. Also Grafen gibt es schon
sehr lange. Und die Art und Weise,
wie man Grafen lange Zeit betrachtet hat, sind als Matrizen.
Man hat eine N-Mail-N-Matrix und da hat man an der Position eine Eins geschrieben,
wenn der ein I mit dem Knoten J verbunden ist.
Und sonst steht da eine Null drin. Und das können sie machen,
da brauchen sie Endquadrat viel Speicher bei Edden Knoten,
unabhängig davon, wie viele Kanten sie haben.
Und wenn sie einen Ungerichteten Grafen haben, ist die Matrix
sogar symmetrisch, klar. Beim gerichteten Grafen können sie
die Transponierte bilden und dann drehen sie alle Kanten um,
ist auch hübsch. Und sie können sehr effizient testen,
ob zwei Knoten mit einer Kante verbunden sind.
Es hat lange gedauert, bis in die 80er Jahre,
bis jemand gesagt hat, warum speichern wir Grafen eigentlich so,
lass uns doch mal
Listen verwenden
Ja, und dann kam Adjazenzlisten auf,
man hat einfach pro Knoten eine Liste und ein Dastehen dran,
steht einfach eine Liste mit dem benachbarten Knoten, also den Adjazentenknoten.
Das ist die Adjazenzliste, die sogenannte,
der Speicherbedarf ist dann geringer, man hat nur noch N plus M-Speicher,
weil man ja für jede Kante nur zwei Werte speichern muss und das ist vorteilhaft.
Man braucht ein bisschen länger, um zu prüfen, ob zwei Knoten miteinander verbunden ist.
Man muss ja jetzt noch eine Liste durchlaufen,
dann hat man erst mal das Gefühl,
das ist ungeschickt, ja, aber wir werden sehen,
dass die allermeisten Algorithmen bei der Verwendung von Adjazenzlisten schneller werden,
Sie müssen sich überlegen, bei den meisten Algorithmen müssen wir alle Kanten angucken.
Und alle Akkanten angucken, kostet hier immer N-Quadratzeit.
Selbst wenn sie ganz wenig Kanten haben. Sie müssen die
ganze Matrix immer durchlaufen. Und hier kostet es nur N plus M-Zeit.
Und nur damit sie nicht denken, dass wir hier Kleinkram,
wenn sie Netzwerke aufbauen, wir bauen heute riesige Netzwerke auf.
Alle Facebook-Accounts können sie vernetzen über Freundschaftslinks. Wenn sie das
mit einer quadratischen Matrix darstellen würden, wären sie platt.
Da haben sie, ich weiß nicht,
wie viele User da momentan drin sind, 250 Millionen oder so,
können sie nicht machen.
Mit Adjazenzlisten ist das kein Problem. Können sie machen
Ok, Knoten und Kanten haben dann anwendungsspezifische Informationen.
Ich zeige hier nur noch ganz schnell das Bild hier und dann
sind wir fertig. Also ein Beispiel, ungerichteter Graf,
das sind die Addizenzlisten dazu. Knoten 2 hat Nachbarn 3,
4, 4, 5, 1 und so weiter.
Hier ist die Matrix dazu. Sie sehen, wo die
einzeln sind, sind Kanten. Das ist der gerichtete Fall.
Da sind die Listen ein bisschen kürzer, weil wir immer
nur in der Liste des ausgehenden Knoten die Kante speichern.
Das ist die zugehörige Matrix. Und damit können wir die darstellen
. Und damit haben wir alles, was wir brauchen,
um in der nächsten Vorlesung uns Graf-Algorithmen anzuschauen.
Das war es dann für heute