Chans Algorithmus
Chans Algorithmus (englisch Chan’s algorithm) ist in der algorithmischen Geometrie ein ausgabesensitives Paradigma zur Berechnung der konvexen Hülle einer Menge von Punkten der Euklidischen Ebene oder des Raumes. Er kombiniert in einem Divide-and-conquer-Ansatz verschiedene bekannte Algorithmen, um eine asymptotisch optimale Laufzeit zu erzielen. Der Algorithmus ist benannt nach Timothy M. Chan<ref name="Chan">Vorlage:Cite book/URLVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/Meldung2 </ref>, zeitgleich und unabhängig von ihm hat Frank Nielsen dieselbe Methode in seiner Dissertation entwickelt.<ref>F. Nielsen: Algorithmes géométriques adaptatifs. Dissertation (franz.), Université de Nice Sophia-Antipolis, 1996.</ref><ref> </ref>
Problemstellung[Bearbeiten]
Gegeben sei eine Menge <math>S \subset E^2</math> von <math>n</math> Punkten der Euklidischen Ebene, es ist die konvexe Hülle <math>\operatorname{conv}(S)</math> zu berechnen. Hierbei wird die Hülle mit den Eckpunkten ihres Randes identifiziert, mit dieser Setzung gilt dann <math>\operatorname{conv}(S) \subseteq S</math>. Üblicherweise bezeichnet man die Mächtigkeit <math>|\operatorname{conv}(S)|</math> der Hülle mit <math>h</math>, es ist also <math>h \le n</math>. Für das Problem der konvexen Hülle sind verschiedene Lösungen bekannt, diese fallen stets in eine von zwei Kategorien: Bei den ausgabesensitiven Algorithmen beeinflusst sowohl die Eingabe- als auch die Ausgabegröße die Laufzeit; bei den nicht ausgabesensitiven Algorithmen hängt die Laufzeit dagegen ausschließlich von der Eingabegröße ab. Ein bekanntes Beispiel für eine ausgabesensitive Methode ist Jarvis’ March, die in Zeit <math>O(nh)</math> läuft; der bekannteste nicht ausgabesensitive Algorithmus ist Graham’s Scan mit Laufzeit <math>O(n \log n)</math> (siehe auch Landau-Notation).
Idealerweise würde man also gern für Instanzen mit kleiner Hülle eine ausgabesensitive Lösung wählen, während man für Eingaben mit vielen Punkten auf der Hülle eher einen nicht ausgabesensitiven Algorithmus bevorzugt. Allerdings ist die Größenordnung der Ausgabe üblicherweise unbekannt. Zusätzlich lässt sich im ausgabesensitiven Fall eine untere Laufzeitschranke beweisen, die mit <math>\Omega(n \log h)</math> noch deutlich unter der Laufzeit von Jarvis’ March liegt.<ref name="KirkpatrickSeidel">Vorlage:Cite book/URLVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/MeldungVorlage:Cite book/Meldung2 </ref> Chans Algorithmus gibt nun eine gemeinsame Methode für alle Eingaben an, die außerdem die optimale Laufzeit von <math>O(n \log h)</math> erreicht.
Algorithmus[Bearbeiten]
Die Funktionsweise von Chans Algorithmus beruht auf der folgenden Beobachtung: Soll per Jarvis’ March die konvexe Hülle einer Menge <math>P \subset E^2</math> bestimmt werden, so wird stets ausgehend von einem bereits bekannten Punkt <math>p_i</math> der konvexen Hülle ein weiterer Punkt <math>p_{i+1} \in P</math> gesucht, so dass ganz <math>P</math> links von der Strecke <math>\overline {p_i\, p_{i+1}}</math> liegt. Normalerweise benötigt dies eine Rechenzeit in <math>O(|P|)</math>. Ist aber <math>\operatorname{conv}(P)</math> bereits bekannt, reduziert sich der Aufwand durch binäre Suche auf <math>O(\log |P|)</math>. Die Definition von links bezieht sich dabei auf die Orientierung der Euklidischen Ebene und muss im dreidimensionalen Fall entsprechend angepasst werden.
Daraus ergibt sich folgende Berechnungsvorschrift:
Es sei eine natürliche Zahl <math>m \le n</math> gegeben.
- Teile <math>S</math> in (circa) <math>n/m</math> Teilmengen <math>S_j</math> mit jeweils höchstens <math>m</math> Punkten auf.
- Für jede der Mengen <math>S_j</math> berechne <math>\operatorname{conv}(S_j)</math> via Graham’s Scan.
- Setze die Teillösungen via Jarvis’ March wie folgt zusammen: Solange <math> i \le m</math> gilt,
- bestimme für den gerade aktuellen Punkt <math>s_i</math> der konvexen Hülle und jede Teilmenge <math>S_j</math> einen Kandidaten <math>s_{i+1,j}</math>, so dass <math>S_j</math> vollständig links der Strecke <math>\overline {s_i\, s_{i+1,j}}</math> liegt;
- bestimme aus den Kandidaten einen Punkt <math>s_{i+1}</math>, so dass die Menge <math>\{s_{i+1,j} \mid 1 \le j \le n/m \}</math> vollständig links der Strecke <math>\overline {s_i\, s_{i+1}}</math> liegt.
Für <math>m \ge h</math> erhält man durch diese Berechnung die gesamte konvexe Hülle der ursprünglichen Menge <math>S</math>, andernfalls bricht der Algorithmus zu früh ab. Entscheidend dabei ist, dass das Eintreten dieses Falles detektiert werden kann, ohne <math>h</math> zu kennen. Die Berechnung in Schritt 3 kehrt nämlich nur dann zum fest gewählten Startpunkt <math>s_1</math> zurück, wenn die gesamte Hülle gefunden wurde.
Es lässt sich nun eine vorläufige Laufzeitanalyse in Abhängigkeit von <math>m</math> durchführen. Als nicht ausgabesensitiver Algorithmus benötigt Graham’s Scan im 2. Schritt für jedes der <math>S_j</math> Zeit in <math>O(m \log m)</math>, insgesamt also <math>n/m \cdot O(m \log m) = O(n \log m)</math>. Da die konvexen Hüllen der Teilmengen bereits vorliegen, beschleunigt sich im Schritt 3.1 die Berechnung aller Kandidaten auf insgesamt <math>O((n/m) \log m)</math>. Schritt 3.2 benötigt sogar nur Zeit linear in <math>n/m</math> und ist daher asymptotisch vernachlässigbar. Beide Teilschritte werden je <math>m</math> Mal ausgeführt, es ergibt sich daher auch für den 3. Schritt eine Laufzeit in <math>O(n \log m)</math>. Insgesamt benötigt die Berechnung also Zeit <math>O(n \log m)</math>.
Könnte man nun einfach <math>m = h</math> setzen, erhielte man einen optimalen Algorithmus, wie eingangs erwähnt ist die Größe der Ausgabe jedoch unbekannt. Diesem Problem kann man durch iterative Durchläufe mit immer größeren Werten für <math>m</math> begegnen. Sobald das erste Mal <math>m \ge h</math> gewählt wurde, kann die Berechnung beendet werden. Dabei ist die Geschwindigkeit der Erhöhung relevant für die Gesamtlaufzeit, man möchte weder zu viele Iterationen (durch zu langsames Erhöhen) noch zu lange Iterationen (durch zu schnelles Erhöhen). Chans Algorithmus beginnt dabei mit einem konstanten Wert für <math>m</math> und quadriert diesen in jedem Durchlauf. Entsprechend werden nur (circa) <math>\log \log h</math> viele Iterationen benötigt, bis <math>m</math> die Größe der Ausgabe das erste Mal überschreitet. Für die finale Laufzeit ergibt sich nun
- <math>\sum_{t = 1}^{\log \log h} O(n \log 2^{2^t}) = O(n) \cdot \sum_{t = 1}^{\log \log h} O(2^t) = O(n \cdot 2^{\log \log h}) = O(n \log h)</math>,
wie gewünscht.
Einzelnachweise[Bearbeiten]
<references />