it-swarm.com.de

Was ist der effizienteste Algorithmus, um eine gerade Linie zu finden, die die meisten Punkte durchläuft?

Das Problem:

N Punkte sind auf einer 2-dimensionalen Ebene angegeben. Was ist die maximale Anzahl von Punkten auf derselben Gerade Linie?

Das Problem hat O (N2) Lösung: Gehen Sie jeden Punkt durch und ermitteln Sie die Anzahl der Punkte, die denselben dx / dy in Bezug auf den aktuellen Punkt haben. Speichern Sie dx / dy-Beziehungen in einer Hash-Map, um die Effizienz zu erhöhen.

Gibt es eine bessere Lösung für dieses Problem als O (N2)

46
Leonid

Es gibt wahrscheinlich keine Lösung für dieses Problem, die in einem Standardberechnungsmodell wesentlich besser ist als O (n ^ 2).

Das Problem des Auffindens von drei kollinearen Punkten reduziert sich auf das Problem des Auffindens der Linie, die durch die meisten Punkte geht, und das Auffinden von drei kollinearen Punkten ist 3SUM-hard, was bedeutet, dass das Lösen dieser Linie in weniger als 0 (n ^ 2) Zeiten von großer Bedeutung wäre theoretisches Ergebnis.

Siehe vorherige Frage zum Finden von drei Kollinearpunkten.

Angenommen, wir möchten ein 3SUM-Problem beantworten, z. B. x, y, z in der Liste X suchen, so dass x + y + z = 0 ist. Wenn wir einen schnellen Algorithmus für das Kollinearpunktproblem hätten Mit diesem Algorithmus können wir das 3SUM-Problem wie folgt lösen. 

Erstellen Sie für jedes x in X den Punkt (x, x ^ 3) (für den Moment nehmen wir an, dass die Elemente von X verschieden sind). Prüfen Sie anschließend, ob drei kollineare Punkte unter den erstellten Punkten vorhanden sind.

Um zu sehen, dass dies funktioniert, beachten Sie, dass wenn x + y + z = 0 ist, die Steigung der Linie von x nach y ist

(y ^ 3 - x ^ 3)/(y - x) = y ^ 2 + yx + x ^ 2

und die Steigung der Linie von x nach z ist

(z ^ 3 - x ^ 3)/(z - x) = z ^ 2 + zx + x ^ 2 = (- (x + y)) ^ 2 - (x + y) x + x ^ 2. = x ^ 2 + 2xy + y ^ 2 - x ^ 2 - xy + x ^ 2 = y ^ 2 + yx + x ^ 2

Wenn umgekehrt die Steigung von x nach y gleich der Steigung von x nach z ist, dann

y ^ 2 + yx + x ^ 2 = z ^ 2 + zx + x ^ 2,

was bedeutet das

(y - z) (x + y + z) = 0,

daher ist entweder y = z oder z = -x - y ausreichend, um die Gültigkeit der Reduktion zu beweisen.

Wenn in X Duplikate vorhanden sind, prüfen Sie zuerst, ob x + 2y = 0 für ein beliebiges x ist und das Element y (in linearer Zeit mithilfe von Hashing oder O (n lg n) -Zeit mit Sortierung), und entfernen Sie dann die Duplikate, bevor Sie auf die Option "." kollineares Zielsuchproblem.

38
jonderry

Wenn Sie das Problem auf Linien begrenzen, die durch den Ursprung verlaufen, können Sie die Punkte in Polarkoordinaten (Winkel, Abstand vom Ursprung) konvertieren und nach dem Winkel sortieren. Alle Punkte mit demselben Winkel liegen auf derselben Linie. O (n logn) 

Ich glaube nicht, dass es im allgemeinen Fall eine schnellere Lösung gibt.

4
Erwin J.

Die Hough-Transformation kann Ihnen eine ungefähre Lösung geben. Dies ist ein ungefährer Wert, da die Binning-Technik im Parameterbereich eine begrenzte Auflösung hat, so dass Sie durch den maximalen Bin einen begrenzten Bereich möglicher Zeilen erhalten. 

4
ergosys

Wie bereits erwähnt, gibt es wahrscheinlich keine Möglichkeit, den allgemeinen Fall dieses Problems besser als O (n ^ 2) zu lösen. Wenn Sie jedoch davon ausgehen, dass eine große Anzahl von Punkten auf derselben Linie liegt (sagen Sie, die Wahrscheinlichkeit, dass ein zufälliger Punkt in der Punktmenge auf der Linie mit der höchsten Punktzahl liegt, ist p), benötigen Sie keinen genauen Algorithmus Ein randomisierter Algorithmus ist effizienter.

maxPoints = 0
Repeat for k iterations:
    1. Pick 2 random, distinct points uniformly at random
    2. maxPoints = max(maxPoints, number of points that lies on the 
       line defined by the 2 points chosen in step 1)

Wenn Sie im ersten Schritt 2 Punkte ausgewählt haben, die auf der Linie mit der höchsten Punktzahl liegen, erhalten Sie die optimale Lösung. Angenommen, n ist sehr groß (d. H. Wir können die Wahrscheinlichkeit, zwei wünschenswerte Punkte als Abtastung mit Ersatz zu finden, behandeln), ist die Wahrscheinlichkeit, dass dies geschieht, p ^ 2. Daher ist die Wahrscheinlichkeit, nach k Iterationen eine suboptimale Lösung zu finden, (1 - p ^ 2) ^ k. 

Angenommen, Sie können eine falsche Rate für negative Rate tolerieren. Dann läuft dieser Algorithmus in O(nk) = O (n * log (err)/log (1 - p ^ 2)). Wenn sowohl n als auch p groß genug sind, ist dies wesentlich effizienter als O (n ^ 2). (Das heißt, n = 1.000.000, und Sie wissen, dass es mindestens 10.000 Punkte auf derselben Linie gibt. Dann wäre n ^ 2 für die Größe von 10 ^ 12 Operationen erforderlich, während der randomisierte Algorithmus die Größe von 10 ^ 9 Operationen erfordern würde um eine Fehlerrate von weniger als 5 * 10 ^ -5 zu erhalten.)

0
Tony Cai

Wieder eine O (n ^ 2) -Lösung mit Pseudocode. Idee ist das Erstellen einer Hashtabelle mit der Zeile selbst als Schlüssel. Die Linie wird durch die Neigung zwischen den beiden Punkten definiert, der Punkt, an dem die Linie die X-Achse schneidet, und der Punkt, an dem die Linie die Y-Achse schneidet.

Die Lösung geht von Sprachen wie Java und C # aus, wobei für die Hash-Funktion Equals-Methode und Hashcode-Methoden des Objekts verwendet werden. 

Erstellen Sie ein Objekt (Aufruf SlopeObject) mit 3 Feldern

  1. Steigung // kann unendlich sein
  2. Schnittpunkt mit x-Achse - poix // wird sein (Unendlich, etwas y-Wert) oder (x-Wert, 0)
  3. Anzahl

poix ist ein Punktepaar (x, y). Wenn die Linie die x-Achse kreuzt, wird poix (einige Zahl 0). Wenn die Linie parallel zur x-Achse ist, dann ist poix = (unendlich viele Zahl), wobei der y-Wert die Stelle ist, an der die Linie die y-Achse kreuzt. Überschreiben ist die Methode, bei der 2 Objekte gleich sind, wenn Slope und poix gleich sind.

Hashcode wird mit einer Funktion überschrieben, die Hashcode basierend auf der Kombination der Werte von Slope und poix bereitstellt. Einige Pseudocodes unten

Hashmap map;
foreach(point in the array a) {
    foeach(every other point b) {
        slope = calculateSlope(a, b);
        poix = calculateXInterception(a, b);
        SlopeObject so = new SlopeObject(slope, poix, 1); // Slope, poix and intial count 1.
        SlopeObject inMapSlopeObj = map.get(so);
        if(inMapSlopeObj == null) {
            inMapSlopeObj.put(so);
        } else {
            inMapSlopeObj.setCount(inMapSlopeObj.getCount() + 1);
        }
    }
}
SlopeObject maxCounted = getObjectWithMaxCount(map);
print("line is through " + maxCounted.poix + " with slope " + maxCounted.slope);
0