Dichtestes Punktpaar

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

Das Problem des dichtesten bzw. engsten Punktpaares (auch closest pair problem) ist die Suche nach den zwei am dichtesten beieinander liegenden Punkten in einer Ebene.

Formale Beschreibung[Bearbeiten | Quelltext bearbeiten]

Gegeben ist eine Menge von Punkten und gesucht sind zwei Punkte so, dass der euklidische Abstand minimal ist.

Brute-force-Algorithmus[Bearbeiten | Quelltext bearbeiten]

Die naive Herangehensweise ist, einfach sämtliche Distanzen zwischen allen möglichen Punktpaaren zu berechnen und daraus die kleinste zu wählen:

minDist = unendlich
für alle p in P:
 für alle q in P:
  wenn p ≠ q und dist(p,q) < minDist:
   minDist = dist(p,q)
   dichtestesPaar = (p,q)
return dichtestesPaar

Da sowohl die innere als auch die äußere Schleife n-mal abgearbeitet werden muss, ist die Laufzeit des Algorithmus .

Divide-&-Conquer-Algorithmus[Bearbeiten | Quelltext bearbeiten]

Zunächst ist die gegebene Punktmenge einmal nach x- und einmal nach y-Koordinaten zu sortieren; man erhält zwei sortierte Listen und .

Der Divide-Schritt teilt in zwei Hälften und links und rechts des Medians auf.

Der rekursive Aufruf geschieht nun jeweils auf die beiden Hälften; man erhält das jeweils dichteste Punktpaar in beiden Hälften, ohne dass eventuelle Punktpaare zwischen beiden Hälften berücksichtigt werden.

Der Conquer-Schritt kombiniert nun diese beiden Hälften, es wird das Minimum der gefundenen Distanzen ausgewählt. Hierbei sind zwei Fälle zu beachten:

  1. Einerseits könnte der kleinste Abstand direkt aus dem linken oder rechten Teilfeld abgelesen werden. In diesem Fall gibt es keine weiteren Probleme.
  2. Es könnte aber auch sein, dass zwei Punkte in unterschiedlichen Hälften existieren, deren Abstand kleiner ist, als der bisher in den Hälften gefundene - dieser Fall ist nun gesondert zu berücksichtigen:
    Prüfung der Nachbarn von P

Im Prinzip wäre es nötig, sämtliche solcher Punktpaare einzeln durchzuprüfen. Da dies allerdings immer noch quadratisch viele sind, würde sich so kein Vorteil in der Laufzeit ergeben (man erhielte als Rekursionsgleichung T(n) = 2·T(n/2) + n²/4, was immer noch in ist). Sinnvoller ist es, zu verwenden, dass bereits eine gewisse „Vorgabe“ für den kleinsten Abstand bekannt ist. Einerseits müssen jetzt nur noch Punkte betrachtet werden, deren Abstand in x-Richtung zur Teilungsgrenze höchstens beträgt; außerdem müssen für diese Punkte auch nur noch Partner betrachtet werden, deren Abstand in y-Richtung kleiner als ist.
So ergibt sich für jeden zu prüfenden Punkt P, dass lediglich konstant viele potentielle Partner zu prüfen sind: Denkt man sich ein Gitter der Weite in der Umgebung der Teilungsgrenze, so kann in jedem Gitterfeld nur ein liegen, da sonst bereits ein Abstand kleiner gefunden worden wäre. Da nur diejenigen Zellen zu prüfen sind, die von P aus höchstens Abstand haben (maximal 12 Stück jeweils oben und unten), sind nun auch maximal 24 weitere Abstände für jeden Punkt zu berechnen, womit statt quadratischer Laufzeit nur noch lineare Laufzeit notwendig ist. Insgesamt ist die Rekursionsgleichung nun T(n) = 2·T(n/2) + 24·n, was in (n log n) ist.

Randomisierter Algorithmus[Bearbeiten | Quelltext bearbeiten]

Funktionsweise[Bearbeiten | Quelltext bearbeiten]

Der randomisierte Algorithmus beruht darauf, dass die Punkte in zufälliger Reihenfolge in eine Hashtabelle eingefügt werden, wobei das Einfügen eines Punktes ebenso wie das Testen, ob er die vorher bekannte Minimaldistanz unterbietet, in erwartet O(1) funktioniert. Die Hashtabelle bildet alle Gitterzellen der Größe (δ/2)² auf den eventuell darin liegenden Punkt ab. Es muss zwar bei jedem solchen Update von die Hashtabelle neu aufgebaut werden, die Gesamtzahl der Einfügeoperationen in sämtliche dieser Hashtabellen liegt aber trotzdem in O(n).
Ohne Beachtung, wie die Hashtabelle konkret genutzt wird, ergibt zunächst folgender Algorithmus:

Bilde eine zufällige Reihenfolge der Punkte 
δ = d()
Füge  und  in die Hashtabelle ein (Gittergröße δ/2)
Für i = 3..n
  Falls  eine Distanz  zu einem der  hat:
    δ := δ'
    Baue die Hashtabelle neu auf (Gittergröße δ'/2)
  Füge  in die Hashtabelle ein

Gitterstruktur und Hashfunktion[Bearbeiten | Quelltext bearbeiten]

Man kann vereinfachend annehmen, dass alle Punkte Koordinaten zwischen (0,0) und (1,1) haben, ggf. kann hierfür eine Koordinatentransformation vorgenommen werden. Die Ebene, in der die Punkte liegen, wird sodann durch ein Gitter der Weite δ/2 überdeckt. Es wird nun eine universelle Hashfunktion benötigt; sie ermöglicht es einerseits, von einer gegebenen Gitterzelle in O(1) festzustellen, ob sich in dieser Gitterzelle einer der Punkte befindet, und andererseits neue Punkte in O(1) in die bestehende Hashtabelle einzufügen.

Einfügen neuer Punkte[Bearbeiten | Quelltext bearbeiten]

Einfügen von P

Bei jedem Einfügen eines neuen Punktes P ist zu prüfen, ob dadurch die bereits bekannte Minimaldistanz δ unterschritten wird. Anhand der Koordinaten von P lässt sich sofort ablesen, in welche der Gitterzellen P fallen wird. Aufgrund der Aufteilung in Gitterzellen der Größe (δ/2)² muss nur festgestellt werden, ob in einer der umliegenden Gitterzellen schon ein Punkt liegt. Umliegend sind dabei alle Gitterzellen, die eine solche Distanz begründen könnten, also nur der umliegende 5x5-Bereich - alle Punkte die außerhalb dieses Bereichs liegen können keine Distanz <δ zu P haben, da sie aufgrund der Gittergröße mindestens 2·(δ/2) weit weg von P sind. Da in jeder Gitterzelle nur ein einziger Punkt liegen kann (sonst wäre vorher bereits eine Distanz <δ gefunden worden), müssen also auch nur höchstens 25 Punkte geprüft werden. Sofern in diesem Bereich keine Punkte liegen, kann P bedenkenlos in die bestehende Hashtabelle eingefügt werden.
Sind jedoch in diesem Bereich bereits weitere Punkte vorhanden, so wird die Distanz δ aktualisiert auf die neue gefundene Minimaldistanz gesetzt. Dies hat zur Folge, dass die bisherige Hashtabelle nutzlos geworden ist, weil ihre Gitterweite nicht mehr mit δ/2 übereinstimmt. Wir benötigen also eine neue Hashtabelle mit korrektem δ - wir erstellen einfach eine solche Tabelle für alle Punkte bis zu P von Grund auf neu, der Effizienz halber kann man sich beim Neuerstellen die Distanzprüfung sparen, da ja die Minimaldistanz δ aller Punkte bis hin zu P bereits bekannt ist.
Im Endeffekt hat man nach dem Einfügen eines neuen Punktes immer eine korrekte Hashtabelle mit Gitterweite δ/2 und in jedem Gitterquadrat liegt höchstens ein Punkt.

Laufzeitanalyse[Bearbeiten | Quelltext bearbeiten]

Relevant für die Laufzeit ist lediglich die Anzahl der Einfüge-Operationen neuer Punkte. Trivialerweise muss jeder Punkt mindestens einmal eingefügt werden, sie liegt also bei Ω(n).

Fraglich ist jedoch, wie sich der gelegentlich vorkommende Neuaufbau der Hashtabelle auswirkt, da hierfür alle bereits bekannten Punkte erneut eingefügt werden müssen. Hierfür ist es notwendig, die Wahrscheinlichkeit anzugeben, mit der beim Einfügen des i-ten Punkts ein Neuaufbau erforderlich wird, und die dafür notwendigen Kosten einzuberechnen. Intuitiv sieht man, dass mit steigender Punktzahl der Aufwand für die Reorganisation immer größer wird, die Wahrscheinlichkeit dafür aber immer kleiner. Mathematisch betrachtet:

Die Wahrscheinlichkeit dafür, dass der i-te eingefügte Punkt einen Abstand δ' < δ begründet, ist < 2/i, denn dafür müsste genau einer der beiden Punkte sein, die jenes δ' begründen. Da wir insgesamt i Punkte haben, von denen zwei unserer Anforderung entsprechen, ist die Wahrscheinlichkeit höchstens 2/i.

Definieren wir nun die Zufallsvariable . Sie sei 1, falls beim Einfügen des i-ten Punkts δ' < δ gilt, und 0 sonst.

Dann gilt: Die Gesamtanzahl der Einfügeoperationen ist ; also die sowieso einzufügenden Punkte plus die Anzahl der Einfügeoperationen durch die Neuorganisationen.

Für den Erwartungswert lässt sich aufgrund dessen Linearität sagen: .

Das heißt, die Gesamtanzahl der erwarteten Einfügeoperationen bei allen Neuorganisationen der Hashtabelle ist lediglich 2n. Insgesamt ist die erwartete Laufzeit somit O(n + 2n), also linear.

Quellen[Bearbeiten | Quelltext bearbeiten]