un

guest
1 / ?
back to lessons

Was der Kurs behandelt hat und was er übersprungen hat

Hamming's Kurs behandelte: Analog-Digital-Umsetzung, Fehlerkorrektur, Simulation, Statistik, Numerische Mathematik und n-dimensionalen Geometrie. Er dachte rechnerisch - Signalverarbeitung, Codierungstheorie, digitales Filtern erfordern rechnerische Denkweise.

Er hat die algoritmische Komplexität nie systematisch gelehrt.

Growth Curves for Algorithmic Complexity: O(1) flat, O(log N) gentle, O(N) diagonal, O(N²) parabola

Warum gab es den Bereich

Hamming's mentaler Ansatz entstand in einer Ära, in der Hardwarebottlenecks vorherrschten. Die Frage lautete: Wie viele Transistoren pro Chip? Die Algorithmus lief auf dem verfügbaren Hardware. Bei N=100 kostet ein O(N²) Algorithmus 10.000 Operationen. Bei N=1.000 kostet er 1.000.000. Bei N=10.000 (heute in Abhängigkeitsgraphen, sozialen Netzwerken und Paketmanagern routinemäßig): 100.000.000. Der Unterschied zwischen O(N) und O(N²) war in den Skalen, auf denen Hamming gearbeitet hat, unsichtbar und katastrophal in den Skalen, die seine Studenten bewohnen würden.

Donald Knuth veröffentlichte The Art of Computer Programming ab 1968 - im selben Jahr, in dem Hamming am vollsten forschendes Produktivität erreichte. Big O Notation wurde im Laufe der 1970er und 1980er Jahre zum Standardalgorithmusvokabular. Hamming's Kurs datiert auf 1995, aber sein mentaler Ansatz der Rechenmaschine entstand früher. Er hat diesen Abschnitt nicht aktualisiert.

Eine Grundlage nach Hamming's eigener Prüfung

Hamming's Test für eine Grundlage: (1) sie hat eine lange Zeit gehalten, (2) der Rest des Feldes kann mit standardmethoden von ihr abgeleitet werden.

Big O erfüllt beide Tests. Die Wachstumsanalyse hat seit Knuth gehalten. Aus ihr ableiten Sie die Wahl von Algorithmen, die Wahl von Datenstrukturen und die Vorhersage von Leistung - die meisten praktischen Computerwissenschaften fließen aus der Frage 'Wie wächst der Kosten als N wächst?' Hamming hat die grundlegendste Grundlage seines eigenen Feldes vermisst.

Big O als Grundlage

Hamming lehrte, dass Grundlagen länger halten als spezifische Technologien. Er verwendete Kalculus als Beispiel: Erfunden einmal, anwendbar über Physik, Ingenieurwesen, Volkswirtschaftslehre und Biologie für Jahrhunderte. Periphere Werkzeuge kommen und gehen; Grundlagen vermehren sich.

Hamming lehrte, dass Grundlagen länger halten als spezifische Technologien. Zählt die Algoritmische Komplexität als Grundlage nach seinem eigenen Test? Anwenden Sie beide seiner Kriterien: (1) Haltbarkeit und (2) Abgeleitbarkeit - kann der Rest des Feldes von ihr abgeleitet werden? Argumentieren Sie Ihre Position konkret.

Wie sich die Kosten erhöhen

Big O misst, wie sich die Kosten erhöhen, wenn der Eingang wächst. Nicht die konstante Größe - die Geschwindigkeit. Zwei Algorithmen, die beide in 'ein paar Millisekunden' bei N=100 laufen, können sich um einen Faktor von 10.000 unterscheiden, wenn einer in O(N) Zeit und der andere in O(N²) läuft.

Die gängigen Komplexitätsklassen

O(1) - Konstant. Die Kosten bleiben unabhängig von N gleich. Beispiele: Hash-Tabelleintrag nach Schlüssel, Arrayzugriff durch Index, Stackschieben/Ablegen. Verdoppelt N ändert sich nichts.

O(1) Wachstumskurve: flache horizontale Linie

O(log N) - Logarithmisch. Jeder Schritt halbiert die verbleibende Arbeit. Beispiele: Binärsuche in einem sortierten Array, räumliche Abfrage in einem Spielmotor mit BVH, Operationen an einem ausgewogenen binären Baum. Bei N=1.000.000: log₂(1.000.000) ≈ 20 Schritte.

O(log N) Wachstumskurve: schnelle Steigung, dann Flachung

O(N) - Linear. Kosten wachsen mit der Eingabe. Beispiele: lineare Durchsuchung einer Liste, Lesen einer Datei, Zählen von Elementen in einer Sammlung. Bei N=10.000: 10.000 Operationen.

O(N) Wachstumskurve: schräge diagonale Linie

O(N log N) - Linearithmisch. Nahezu linear, aber etwas schlechter. Beispiele: Mergesort, effiziente kürzestpfad-Algorithmen (Dijkstra mit Heap). Bei N=10.000: ~133.000 Operationen.

O(N log N) Wachstumskurve: etwas steiler als linear

O(N²) - Quadratisch. Kosten explodieren. Beispiele: list.contains() in einer Schleife über dieselbe Liste, Bubblesort, naive alle-zu-aller-Vergleich. Bei N=1.000: 1.000.000 Operationen. Bei N=10.000: 100.000.000 Operationen.

O(N²) Wachstumskurve: parabolische Explosion

O(2^N) — Exponential. Unbrauchbar bei jeder bedeutenden Skala. Beispiele: brute-force Kombinatorik, Suche nach allen Teilmengen. Bei N=30: über 1.000.000.000 Operationen.

O(2^N) Wachstumskurve: exponentieller Explosion

Die Skala, bei der es sichtbar wird

N=10 Vergleiche:
  O(N):   10
  O(N²):  100
  Verhältnis:  10x

N=1.000 Vergleiche:
  O(N):   1.000
  O(N²):  1.000.000
  Verhältnis:  1.000x

N=10.000 Vergleiche:
  O(N):   10.000
  O(N²):  100.000.000
  Verhältnis:  10.000x

Bei N=10 fällt der Unterschied kaum auf. Bei N=10.000 läuft das O(N²)-Algorithmus 10.000 mal langsamer. Dies ist, warum MOAD-0001 für Jahrzehnte unsichtbar blieb: Die Graphen, auf denen es 1993 lief, hatten 50 Knoten. Bis 2020 lief der gleiche Code auf Abhängigkeitsgraphen mit 50.000 Knoten.

Klassifizieren Sie drei Operationen

Anwenden Sie die Komplexitätsklassen auf konkrete Operationen. Die Schlüsselfrage für jede: Wie viele Operationen sind erforderlich, wenn N wächst?

Geben Sie für jede Operation die Big O Komplexität & erklären Sie in einer Satz, warum: (a) Das Suchen eines Wortes in einem Wörterbuch durch Seitennummer (b) Die Suche nach jedem Wort in einem Wörterbuch (c) Das Sortieren einer gemischten Kartenstapel durch Versuchen jeder möglichen Reihenfolge Schreiben Sie eine Erklärung für jeden.

Richtiger Code, falsche Wachstumscurve

Richtiger Code, der in O(N²)-Zeit läuft, erzeugt identische Ergebnisse wie O(N)-Code. Die Tests funktionieren. Die Ausgabe passt. Kein Ausnahmefehler wird ausgelöst. Die Fehlersuchverirrung versteckt sich in der Wachstumscurve.

Diese Eigenschaft macht O(N²) Defekte sedimentär: Sie verfestigen sich im Arbeitscode und werden erst sichtbar, wenn N über einen Schwellenwert wächst. Der Code selbst hat sich nicht geändert. Die Welt um ihn herum hat sich geändert.

MOAD-0001: Der sedimentäre Defekt

Das am weitesten verbreitete Beispiel: visited = [] innerhalb einer Schleife für die Durchführung eines Graphen.

visited = []
for node in graph:
    if node not in visited:  # O(N) Scan pro Aufruf
        visited.append(node)
        process(node)

Jeder not in visited-Aufruf scannt 0 bis len(visited)-1 Elemente. N Aufrufe × N/2 durchschnittlich gescannte Elemente = O(N²) insgesamt. Der Fix: Eine Zeile.

visited = set()  # O(1) Mitgliedschaftstest
for node in graph:
    if node not in visited:  # O(1) Hash-Lookup
        visited.add(node)
        process(node)

Selbe Verhaltensweise. Selbe Ausgabe. Selbe Tests, die erfolgreich sind. Die Wachstumsrate ändert sich von O(N²) auf O(N). Bei N=1.000: 1.000× schneller. Bei N=10.000: 10.000× schneller.

Warum Hamming's Era diesen Samen gelegt hat

Im frühen Java und C war ArrayList (dynamische Liste) die Standardsequenzialcontainer. Sets existierten, waren aber nicht idiomatisch - Entwickler griffen nach dem, was ihnen bekannt war. Eine Graphen-Durchführung von 1993 mit N=50 lief in Mikrosekunden mit visited = []. Dieser Code wurde in die Versionskontrolle aufgenommen, kopiert, in Bibliotheken eingewickelt und in Paketmanagern bereitgestellt. Bis 2020 lief dieselbe Musterung auf Abhängigkeitsgraphen mit 50.000 Knoten.

Der Code war in 1993 korrekt. Er wurde teuer, als die Welt um ihn herum sich änderte. Hamming's Era hat diesen Defektklasse gesät, indem er nie die Wachstumsrate-Frage benannte.

Diagnose & Fix

Anwenden Sie die Diagnose: Finden Sie das O(N²)-Muster, identifizieren Sie den Fix.

Sie finden diesen Code in einer Produktionsbasis: `if item not in visited_list: visited_list.append(item)` innerhalb einer Schleife über 50.000 Elemente. Wie viele Operationen führt der Mitgliedschaftstest im Durchschnitt über die gesamte Schleife durch und was würden Sie `visited_list` ersetzen, um es zu beheben?

Welche Namensänderungen

Bevor Big O einen Namen hatte, bemerkten Programmierer, dass 'das langsam läuft'. Sie profilten. Sie reimplementierten. Aber sie konnten das Muster nicht lehren - sie konnten nur auf Instanzen zeigen. Ein Muster ohne Namen kann nicht weitergegeben werden.

Nachdem Big O einen Namen hatte, konnte ein erfahrener Ingenieur sagen: 'Das ist O(N²), ersetzen Sie es mit einem Satz' und ein junger Ingenieur konnte verstehen, anwenden und das Muster weitergeben. Der Name machte das Muster zu einem übertragbaren Einheit der Kenntnisse.

Hamming kannte diesen Mechanismus in anderen Domänen. Er beschrieb, wie der Name 'Spaghetticode' in den 1960er Jahren die Gemeinschaft dazu brachte, Mängel zu erkennen, zu diskutieren und letztendlich eine Klasse von Fehlern zu beseitigen, die alle erfahren hatten, aber keinen Namen hatten. Das gleiche Prinzip gilt für Komplexitätsklassen.

Unhamming fügt den Vokabular hinzu, den Hamming ausgelassen hat: O(1), O(log N), O(N), O(N log N), O(N²), O(2^N). Diese Namen lassen Sie das Wachstumsverhalten in dem, was Sie lesen, Vorhersagen bei Skalierung und Kommunikation von Optimierungen genau. Sie erfüllen Hamming eigenen Test für eine Grundvoraussetzung: dauerhaft und erzeugend den überwiegenden Teil des Restfeldes.

Von der Zahlentheorie zur Informatik

Big O Notation ist nicht in der Informatik entstanden. Sie stammt aus reinen Mathematik - speziell aus der Zahlentheorie - 74 Jahre vor Donald Knuth, der sie für Algorithmen adaptierte.

Paul Bachmann, 1894

Paul Bachmann, ein deutscher Mathematiker, führte die O Notation in seinem 1894 erschienenen Buch Die Analytische Zahlentheorie (Analytic Number Theory) ein. Er benötigte eine kompakte Möglichkeit, um auszudrücken, dass eine Größe höchstens so schnell wächst wie eine andere, multipliziert mit einer konstanten Faktor. Schreiben Sie 'f(n) = O(g(n))' bedeutet: f wächst höchstens so schnell wie g. Dies ermöglichte es Mathematikern, Fehlerbeträge in Näherungen zu behandeln, ohne genaue Konstanten zu verfolgen.

Bachmann dachte nicht an Schleifen, Datenstrukturen oder Ausführzeit. Er dachte an die Verteilung von Primzahlen - insbesondere an die Fehlerbeträge in der Primzahleigenschaft.

Edmund Landau, 1909

Edmund Landau, ein weiterer deutscher Mathematiker, popularisierte die Notation in seinem 1909 erschienenen Handbuch der Lehre von der Verteilung der Primzahlen (Handbook on the Distribution of Prime Numbers). Landau führte auch die verwandten Notationen o (klein-o) ein und verwendete die Familie der asymptotischen Symbole so weitreichend, dass die Familie als Bachmann-Landau-Notation oder einfach als Landau-Notation bekannt wurde.

Für sechs Jahrzehnte lebte die O-Notation ausschließlich in der Mathematik. Kein Programmierer dachte daran.

Donald Knuth, 1968 & 1976

Donald Knuth übertrug die Notation in die Informatik. In The Art of Computer Programming (Band 1, 1968) verwendete Knuth die O-Notation, um die Laufzeit von Algorithmen zu beschreiben - eine direkte Übertragung von Bachmanns Werkzeug in einen neuen Bereich. Er war der Erste, der sie systematisch für die Analyse von Algorithmen anwandte.

1976 veröffentlichte Knuth einen kurzen Artikel in SIGACT News mit dem Titel 'Big Omicron and Big Omega and Big Theta'. Er führte Omega (Omega) für untere Grenzen und Theta für enge Grenzen ein und vervollständigte die dreisymbolige Vokabel, die heute die Informatik verwendet. Er argumentierte auch dafür, die Verwendung dieser Symbole in der Branche zu standardisieren - ein bewusstes Standardisierungsverfahren, das die Einführung beschleunigte.

Bis in die frühen 1980er Jahre war Big O in jedem Algorithmenlehrbuch enthalten. In den 1990er Jahren war es Standardvokabular für Interviews.

Eine 74-jährige Reise

1894 — Bachmann führt O in der Zahlentheorie ein
1909 — Landau popularisiert O, o und die gesamte Notationsfamilie
1953 — Hamming beginnt an den Bell Labs (lernt Big O nie als CS-Tool)
1968 — Knuth verwendet O für die Algorithmusanalyse in TAOCP Band 1
1976 — Knuth fügt Omega und Theta hinzu; argumentiert für Standardisierung
1980er Jahre — Big O tritt in allen CS-Lehrplänen auf
1993 — MOAD-0001 bildet Sedimentsschichten im Produktionscode
1995 — Hamming hält seinen letzten Kurs; deckt Big O nie ab
2026 — MOAD-0001 in 1.000+ Open-Source-Projekten gefunden

Bachmanns Werkzeug verbrachte 74 Jahre in der reinen Mathematik, sichtbar für jeden Mathematiker, aber unsichtbar für jeden Programmierer. Knuth erkannte die Übertragung. Hamming, der in derselben Epoche und in derselben Computergemeinschaft tätig war, machte es nie zu einem Teil seines Kurses.

Warum Knuths Standardisierung wichtig war

Knuths 1976 Artikel tat mehr als nur Omega und Theta einzuführen. Er argumentierte, dass das Feld konsistente Notation benötigte und dass inkonsistente Notation aktiv schädlich war. Verschiedene Lehrbücher verwendeten O, um unterschiedliche Dinge zu bedeuten - manchmal als obere Schranke, manchmal als Näherung, manchmal ohne anzugeben, ob Konstanten relevant waren. Knuth klärte dies auf.

Dies ist die dynamische Bezeichnung nach Hamming angewendet auf die Notation selbst. Bevor Knuth die Symbole standardisierte, konnten Ingenieure die Komplexitätsansprüche genau nicht zwischen Lehrbüchern, Papieren oder Teams kommunizieren. Nach der Standardisierung hatte 'das läuft in O(N log N)' bei jedem, der es sagte, genau einen Bedeutung.

Knuth trug auch die informelle Übersetzung bei: 'O(f(n)) bedeutet, dass die Laufzeit höchstens einen konstanten Faktor mal f(n) beträgt, für alle ausreichend großen n.' Diese Interpretation - obere Schranke, bis auf einen konstanten Faktor, für große Eingaben - wurde zur Standardintuition, die jeder Programmierer lernt.

Bachmann hat die O-Notation für die Zahlentheorie im Jahr 1894 erfunden. Knuth übernahm sie im Jahr 1968 für die Algorithmusanalyse. Was musste sich - in der Informatik, in der Größenordnung oder in der Art und Weise, wie Programmierer arbeiteten - ändern, damit ein rein mathematisches Werkzeug zum unerlässlichen Vokabular für Software-Engineer werden konnte? Zumindest zwei Faktoren.