Punkt-in-Polygon in C

Gepostet: , Zuletzt aktualisiert:

package point_in_polygon.c;

/**
 * Used to perform the Raycasting Algorithm to find out whether a point is in a given polygon.
 */
public class PointInPolygon {
    /**
     * Performs the even-odd-rule Algorithm to find out whether a point is in a given polygon.
     * This runs in O(n) where n is the number of edges of the polygon.
     *
     * @param polygon an array representation of the polygon where polygon[i][0] is the x Value of the i-th point and polygon[i][1] is the y Value.
     * @param point   an array representation of the point where point[0] is its x Value and point[1] is its y Value
     * @return whether the point is in the polygon (not on the edge, just turn < into <= and > into >= for that)
     */
    public static boolean pointInPolygon(int[][] polygon, int[] point) {
        //A point is in a polygon if a line from the point to infinity crosses the polygon an odd number of times
        boolean odd = false;
        // int totalCrosses = 0; // this is just used for debugging
        //For each edge (In this case for each point of the polygon and the previous one)
        for (int i = 0, j = polygon.length - 1; i < polygon.length; i++) { // Starting with the edge from the last to the first node
            //If a line from the point into infinity crosses this edge
            if (((polygon[i][1] > point[1]) != (polygon[j][1] > point[1])) // One point needs to be above, one below our y coordinate
                    // ...and the edge doesn't cross our Y corrdinate before our x coordinate (but between our x coordinate and infinity)
                    && (point[0] < (polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0])) {
                // Invert odd
                // System.out.println("Point crosses edge " + (j + 1));
                // totalCrosses++;
                odd = !odd;
            }
            //else {System.out.println("Point does not cross edge " + (j + 1));}
            j = i;
        }
        // System.out.println("Total number of crossings: " + totalCrosses);
        //If the number of crossings was odd, the point is in the polygon
        return odd;
    }
}

Über den Algorithmus und die Programmiersprache in diesem Snippet:

Punkt-in-Polygon Algorithmus

Das PIP-Problem (Punkt-in-Polygon) ist das Problem, zu bestimmen, ob ein Punkt ein beliebiges Polygon ist. Dies mag für ein einfaches Polygon wie ein Quadrat oder ein Dreieck trivial klingen, wird jedoch mit komplexeren Polygonen wie dem im folgenden Beispiel komplexer. In diesem Beitrag wird der gerade-ungerade Algorithmus, auch Kreuzungsnummernalgorithmus oder Jordan-Algorithmus genannt (da er mit dem Jordan-Kurvensatz bewiesen werden kann) eingeführt.

Beschreibung des Algorithmus

Das Grundprinzip hinter dem Even-Odd aka. Jordan aka. Der Kreuzzahlalgorithmus besteht darin, zu zählen, wie oft eine Linie vom betreffenden Punkt (in einer beliebigen Richtung) eine Kante des Polygons kreuzt. Diese Linie befindet sich immer außerhalb des Polygons an ihrem “Ende” im Unendlichen. Wenn sie sich also am Anfang (dem fraglichen Punkt) innerhalb des Polygons befindet, muss sie das Polygon irgendwann verlassen und eine Kante überqueren. Es kann das Polygon erneut eingeben (siehe Beispiel unten), muss jedoch immer wieder verlassen werden, wodurch die Gesamtzahl der Kreuzungen ungerade wird, wenn sich der Punkt im Polygon befindet. Das Gegenteil ist auch wahr; Wenn die Anzahl der Kreuzungen gerade ist, liegt der Punkt immer außerhalb des Polygons. Dies ist der oben erwähnte Jordan-Kurvensatz.

Der Algorithmus überprüft jede Kante des Polygons in einer Schleife, um festzustellen, ob die Linie vom Punkt bis zur Unendlichkeit diese kreuzt. Im folgenden Beispiel wird diese Linie vom Punkt bis unendlich nach rechts gezeichnet, kann jedoch eine beliebige Richtung haben.

Die Schritte sind:

  1. Für jede Kante im Polygon:
  2. Wenn die Kante die imaginäre Linie vom Punkt bis unendlich kreuzt, erhöhen Sie einen Zähler.
  3. Wenn der Zähler am Ende ungleichmäßig ist, geben Sie true zurück. Andernfalls geben Sie false zurück.

Eine einfache boolesche Variable, die jedes Mal invertiert wird, wenn eine Kreuzung gefunden wird, ist ebenfalls möglich.

Beispiel des Algorithmus

Betrachten Sie das folgende Polygon mit 8 Kanten und zwei Punkten, für die wir bestimmen möchten, ob sie sich im Polygon befinden:

Punkt im Polygon Jordan (gerade-ungerade) Algorithmus Beispiel Polygon und Punkte

Die Schritte, die der Algorithmus für dieses Polygon ausführt, um zu bestimmen, ob sich der erste (grüne) Punkt im Polygon befindet, beginnen an der ersten Kante:

  1. Grüner Punkt kreuzt Kante 8
  2. Der grüne Punkt kreuzt nicht die Kante 1
  3. Grüner Punkt kreuzt Kante 2
  4. Der grüne Punkt kreuzt nicht die Kante 3
  5. Grüner Punkt kreuzt Kante 4
  6. Grüner Punkt kreuzt Kante 5
  7. Grüner Punkt kreuzt nicht die Kante 6
  8. Der grüne Punkt kreuzt nicht die Kante 7
  9. Gesamtzahl der Überfahrten: 4
  10. Gerade Anzahl von Kantenübergängen, daher befindet sich der Punkt nicht im Polygon

Die Schritte, die der Algorithmus für dieses Polygon ausführt, um zu bestimmen, ob sich der zweite (rote) Punkt im Polygon befindet, beginnen an der ersten Kante:

  1. Der rote Punkt kreuzt nicht die Kante 8
  2. Der rote Punkt kreuzt nicht die Kante 1
  3. Der rote Punkt kreuzt nicht die Kante 2
  4. Der rote Punkt kreuzt nicht die Kante 3
  5. Roter Punkt kreuzt nicht die Kante 4
  6. Roter Punkt kreuzt Kante 5
  7. Der rote Punkt kreuzt nicht die Kante 6
  8. Der rote Punkt kreuzt nicht die Kante 7
  9. Gesamtzahl der Überfahrten: 1
  10. Ungleichmäßige Anzahl von Kantenübergängen, daher befindet sich der Punkt im Polygon

Laufzeitkomplexität des Algorithmus

Die Laufzeitkomplexität des Jordan-Algorithmus aka. Crossing Number Algorithmus aka. Der geradzahlige Algorithmus zur Lösung des Punkt-in-Polygon-Problems für einen einzelnen Punkt ist in Bezug auf die Anzahl der Kanten linear. Dies wird deutlich, wenn man sich den Code ansieht, der eine einzelne Schleife über den Kanten enthält, ohne Rekursion oder weitere Funktionsaufrufe oder Schleifen.

Formal ist die Laufzeit *O(| V |), | V |: = Anzahl der Kanten im Polygon *.

Speicherkomplexität des Algorithmus

Die Speicherkomplexität ist auch linear w.r.t. die Anzahl der Kanten, da zusätzlich zum Polygon nur Variablen mit fester Größe gespeichert werden müssen. Darüber hinaus kann der Algorithmus online implementiert werden, sodass keine vergangenen Kanten während der Schleife betrachtet werden müssen, sodass sie aus dem Speicher entfernt werden können (oder vergleichbare Maßnahmen zur Leistungsverbesserung).

C

C ist eine kompilierte Sprache, die für viele Zwecke verwendet wird, obwohl sie hauptsächlich in Systemen zu finden ist, in denen Wichtigkeit wichtig ist. Dies liegt daran, dass C viel Unterstützung auf niedriger Ebene für die Optimierung bietet, auf Kosten der Tatsache, dass einige der praktischen Abstraktionen, die andere Sprachen bieten, nicht vorhanden sind. C tritt daher hauptsächlich in Situationen auf, in denen die verfügbare Rechenleistung gering ist, wie beispielsweise eingebettete Systeme, oder in Situationen, in denen die erforderliche Rechenleistung hoch ist, wie z. B. Simulation oder Deep Learning.

“Hello World” in C

Das Wichtigste zuerst - hier erfahren Sie, wie Sie Ihre erste Codezeile in C ausführen können.

  1. Wenn Sie unter Linux oder Mac arbeiten, laden Sie die neueste Version von GCC, einem C-Compiler, von gcc.gnu.org herunter und installieren Sie sie. Sie können auch eine frühere Version herunterladen, wenn Ihr Anwendungsfall dies erfordert.
  2. Wenn Sie unter Windows arbeiten, können Sie auch GCC installieren, obwohl dies zu Problemen führen kann. Sie haben auch andere Optionen, z. hier.
  3. Öffnen Sie ein Terminal, stellen Sie sicher, dass der Befehl gcc funktioniert (oder der entsprechende Befehl für den von Ihnen verwendeten Compiler) und dass sich der Befehl, den Sie verwenden werden, auf die Version bezieht, die Sie gerade durch Ausführen installiert haben gcc --version. Wenn der Fehler “Befehl nicht gefunden” (oder ähnlich) angezeigt wird, starten Sie die Befehlszeile und, falls dies nicht hilft, Ihren Computer neu. Wenn das Problem weiterhin besteht, finden Sie hier einige hilfreiche Forumfragen für jede Plattform:

  4. Sobald dies funktioniert, kopieren Sie das folgende Snippet in eine Datei mit dem Namen HelloWorld.c:

    #include<stdio.h>
    int main() {
    printf("Hello World\n");
    return 0;
    }
  5. Ändern Sie das Verzeichnis, indem Sie “cd path / to / HelloWorld” eingeben, dann “gcc HelloWorld.c” ausführen, um die Datei zu kompilieren (die den Bytecode erstellt), und dann “. / A.out” ausführen. Dies sollte “Hello World” auf Ihrem Terminal drucken.

Das ist es! Personen, die mehrere Programmiersprachen kennen, werden feststellen, dass die Eintrittsbarriere in C etwas niedriger ist als in Java, obwohl sie niedriger ist, während die Eintrittsbarriere für Python niedriger ist, obwohl sie höher ist. Meine persönliche Beobachtung ist, dass Sprachen auf niedriger und hoher Ebene tendenziell niedrige Eintrittsbarrieren aufweisen, während Sprachen auf mittlerer Ebene höhere Barrieren aufweisen.

Grundlagen in C

Um in C implementierte Algorithmen und Technologien zu verstehen, muss man zunächst verstehen, wie grundlegende Programmierkonzepte in dieser bestimmten Sprache aussehen. Jedes der folgenden Snippets sollte mit den oben genannten Befehlen kompiliert und ausgeführt werden.

Variablen und Arithmetik

Variablen in C sind statisch typisiert, was bedeutet, dass der Inhalt einer Variablen beim Schreiben des Codes angegeben werden muss. Der Datentyp für ganze Zahlen ist beispielsweise “int”. Zahlen mit Dezimalstellen werden je nach erforderlicher Genauigkeit mit “float” oder “double” eingegeben. Der Typ für Text ist “String”.

#include<stdio.h>

int main() {
    int number = 5;
    double decimalNumber = 3.25;
    double result = number * decimalNumber;
    char callout [] = "The number is ";
    // In this instance, the values are concatenated rather than added because one of them is a String.
    printf("%s", callout);
    printf("%f", result);
    printf("\n");
    return 0;
}

Arrays

Arrays in C sind real Arrays (im Gegensatz zu beispielsweise Python, wo sie als Listen implementiert sind). Dies hat zur Folge, dass die Größe beim Erstellen festgelegt werden muss und nicht geändert werden kann, aber auch, dass sie in C effizienter sind als in Python. Im Gegensatz zu Java überprüft C auch keine Array-Grenzen. Wenn Sie auf einen Index zugreifen, der nicht vorhanden ist, liest das Programm alles, was sich an dieser Stelle im Speicher befindet (was wahrscheinlich Kauderwelsch sein wird).

int integers[5];
integers[3] = 12; // Assigning values to positions in the array
printf("%d\n", integers[0]); // will be 0
printf("%d\n", integers[3]); // will be 12
printf("%d\n", integers[6]); // will print something random that happened to be at that location in memory
return 0;

Bedingungen

Wie die meisten Programmiersprachen kann C “if-else” -Anweisungen ausführen. Zusätzlich kann C auch “switch-case” -Anweisungen ausführen.

int value = 5;
    if(value == 5){
        printf("%s\n", "Value is 5");
    } else if(value < 5){
        printf("%s\n", "Value is less than 5");
    } else {
        printf("%s\n", "Value is something else");
    }
    
    switch (value){
        case 1:
            printf("%s\n", "Value is 1");
            break; // Don't go further down the cases
        case 2:
            printf("%s\n", "Value is 2");
            break; // Don't go further down the cases
        case 3:
            printf("%s\n", "Value is 3");
            break; // Don't go further down the cases
        case 4:
            printf("%s\n", "Value is 4");
            break; // Don't go further down the cases
        case 5:
            printf("%s\n", "Value is 5");
            break; // Don't go further down the cases
        default:
            printf("%s\n", "Value is something else");
    }

Der obige C-Code gibt zweimal “Wert ist 5” aus.

Schleifen

C unterstützt “for” -, “while” - und “do while” -Schleifen. Die Anweisungen break undcontinue werden ebenfalls unterstützt. Das folgende Beispiel zeigt die Unterschiede:

int value = 2;
for (int i = 0; i < value; i++) {
    printf("%d\n", i);
}
while (value > 0) {
    printf("%d\n", value);
    value--;
}
do {
    printf("%d\n", value);
    value--;
} while (value > 0);

Dadurch wird Folgendes auf das Terminal gedruckt:

0
1
2
1
0

Beachten Sie die letzte “0”: Sie wird gedruckt, weil in der “do-while” -Schleife im Vergleich zur “while” -Schleife. Der Codeblock wird mindestens einmal ausgeführt, bevor die Bedingung überprüft wird.

Funktionen

Funktionen in C können ähnlich wie Java deklariert werden, erfordern jedoch weniger Boilerplate, da sie nicht Teil von Klassen oder Objekten sein müssen. Hier ist ein minimales Beispiel für eine Funktion:

#include<stdio.h>

int addNumbers(int numberOne, int numberTwo) {
    return numberOne + numberTwo;
}

int main() {
    printf("%d\n", addNumbers(3, 4));
}

Syntax

C erfordert die Verwendung von geschweiften Klammern ({}), um Codeblöcke in Bedingungen, Schleifen, Funktionen usw.; Außerdem sind am Ende der Anweisungen Semikolons erforderlich. Dies kann zwar zu lästigen Syntaxfehlern führen, bedeutet jedoch auch, dass die Verwendung von Leerzeichen für die bevorzugte Formatierung (z. B. Einrücken von Codeteilen) den Code nicht beeinflusst. Beachten Sie, dass die Syntax von C Java sehr ähnlich ist. Die Syntax von Java und viele andere Sprachen, die nach C kamen und / oder von C abgeleitet wurden, kopieren viele Aspekte seiner Syntax.

Fortgeschrittenes Wissen zu C

C wurde erstmals 1972 veröffentlicht, ist statisch typisiert und wurde mit verschiedenen Implementierungen auf viele Plattformen portiert (eine davon ist GCC, die in diesem Artikel vorgestellt wurde). Für weitere Informationen hat C einen großartigen Artikel Wikipedia.