Dados n segmentos de línea, encuentre si dos segmentos se intersecan

Dados n segmentos de línea (p1, q1), (p2, q2), … (pn, qn), encuentre si los segmentos de línea dados se cruzan entre sí o no.

Ejemplo:

Entrada: {{1, 5}, {4, 5}}, {{2, 5}, {10, 1}}, {{3, 2}, {10, 3}}, {{6, 4} , {9, 4}},{{7, 1}, {8, 1}}
Salida: 2
Explicación: Líneas {{1, 5}, {4, 5}}, {{2, 5}, {10 , 1}} se intersecan, así como {{2, 5}, {10, 1}}, {{3, 2}, {10, 3}}.

Hemos discutido el problema de detectar si dos segmentos de línea dados se cruzan o no . En este post, ampliamos el problema. Aquí tenemos n segmentos de línea y necesitamos averiguar si dos segmentos de línea se intersecan o no. 
Algoritmo ingenuo Una solución ingenua para resolver este problema es verificar cada par de líneas y verificar si el par se cruza o no. Podemos comprobar dos segmentos de recta en tiempo O(1) . Por lo tanto, este enfoque toma O(n 2 ). 
Algoritmo de barrido de línea: podemos resolver este problema en O (nLogn)tiempo utilizando el algoritmo de barrido de línea. El algoritmo primero ordena los puntos finales a lo largo del eje x de izquierda a derecha, luego pasa una línea vertical a través de todos los puntos de izquierda a derecha y verifica las intersecciones. Los siguientes son pasos detallados. 
1) Sean n líneas dadas. Debe haber 2n puntos finales para representar las n líneas. Ordena todos los puntos según las coordenadas x. Mientras clasifica, mantenga una bandera para indicar si este punto es el punto izquierdo de su línea o el punto derecho. 
2) Comience desde el punto más a la izquierda. Haga lo siguiente para cada punto 
a) Si el punto actual es un punto a la izquierda de su segmento de línea, verifique la intersección de su segmento de línea con los segmentos justo encima y debajo de él. Y agrega su línea a activesegmentos de línea (segmentos de línea para los que se ve el punto final izquierdo, pero aún no se ve el punto final derecho). Tenga en cuenta que consideramos solo aquellos vecinos que todavía están activos. 
…. b) Si el punto actual es un punto recto, elimine su segmento de línea de la lista activa y verifique si sus dos vecinos activos (puntos justo arriba y abajo) se cruzan entre sí. 
El paso 2 es como pasar una línea vertical desde todos los puntos comenzando desde el punto más a la izquierda hasta el punto más a la derecha. Es por eso que este algoritmo se llama Algoritmo de línea de barrido. La técnica Sweep Line es útil en muchos otros algoritmos geométricos, como calcular el diagrama de Voronoi 2D 
. ¿Qué estructuras de datos se deben usar para una implementación eficiente? 
En el paso 2, necesitamos almacenar todos los segmentos de línea activos. Necesitamos realizar las siguientes operaciones de manera eficiente:  a) Insertar
un nuevo segmento de línea 
b) Eliminar un segmento de línea 
c) Encontrar el predecesor y el sucesor de acuerdo con los valores de las coordenadas y 
Árbol Negro. Con un BST de autoequilibrio, podemos realizar todas las operaciones anteriores en tiempo O (Iniciar sesión). 
Además, en el paso 1, en lugar de ordenar, podemos usar la estructura de datos de montón mínimo. Construir un montón mínimo toma tiempo O(n) y cada operación de extracción mínima toma tiempo O(Iniciar sesión) (ver esto ). 
Pseudocódigo: 
el siguiente pseudocódigo no usa heap. Simplemente ordena la array. 
 

sweepLineIntersection(Points[0..2n-1]):
1. Sort Points[] from left to right (according to x coordinate)

2. Create an empty Self-Balancing BST T. It will contain 
  all active line Segments ordered by y coordinate.

// Process all 2n points 
3. for i = 0 to 2n-1

    // If this point is left end of its line  
    if (Points[i].isLeft) 
       T.insert(Points[i].line())  // Insert into the tree

       // Check if this points intersects with its predecessor and successor
       if ( doIntersect(Points[i].line(), T.pred(Points[i].line()) )
         return true
       if ( doIntersect(Points[i].line(), T.succ(Points[i].line()) )
         return true

    else  // If it's a right end of its line
       // Check if its predecessor and successor intersect with each other
       if ( doIntersect(T.pred(Points[i].line(), T.succ(Points[i].line()))
         return true
       T.delete(Points[i].line())  // Delete from tree

4. return False

Ejemplo: 
Consideremos el siguiente ejemplo tomado de aquí . Hay 5 segmentos de recta , , y . Las líneas verdes punteadas muestran líneas de barrido. 
 

sweepline

Los siguientes son los pasos seguidos por el algoritmo. Todos los puntos de izquierda a derecha se procesan uno por uno. Mantenemos un árbol de búsqueda binaria autoequilibrado. 

1 se inserta en el árbol. El árbol contiene. Sin intersección. 

La intersección de y está marcada. se inserta en el árbol. Intersección de 1 y 2 encontrado («Tenga en cuenta que el pseudocódigo anterior regresa en este punto»). Podemos continuar desde aquí para informar todos los puntos de intersección. El árbol contiene , . 

La intersección de con está marcada. Sin intersección. se inserta en el árbol. El árbol contiene , , . 

se elimina del árbol. La intersección de y está marcada. Se informa de la intersección de y. El árbol contiene,  

 Las intersecciones de línea con líneas y están marcadas. Sin intersección. se inserta en el árbol. El árbol contiene , , . 

  La intersección de con está marcada. Sin intersección. se inserta en el árbol. El árbol contiene. 

se elimina del árbol. El árbol contiene , , . 

se elimina del árbol. El árbol contiene , , . La intersección de con está marcada. Se informa la intersección de with (Tenga en cuenta que la intersección de 2 y 3 se informa nuevamente. Podemos agregar algo de lógica para verificar si hay duplicados). El árbol contiene , .

Ambos se eliminan del árbol y el árbol se vacía. 

 

C++14

#include <bits/stdc++.h>
using namespace std;
 
// A point in 2D plane
struct Point
{
    int x, y;
};
 
// A line segment with left as Point
// with smaller x value and right with
// larger x value.
struct Segment
{
    Point left, right;
};
 
 
// An event for sweep line algorithm
// An event has a point, the position
// of point (whether left or right) and
// index of point in the original input
// array of segments.
struct Event {
    int x, y;
    bool isLeft;
    int index;
    Event(int x, int y, bool l, int i) : x(x), y(y), isLeft(l), index(i) {}
 
    // This is for maintaining the order in set.
    bool operator<(const Event& e) const {
            if(y==e.y)return x<e.x;
            return y < e.y;
    }
};
 
 
// Given three collinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Point p, Point q, Point r)
{
    if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
        q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
       return true;
 
    return false;
}
 
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are collinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
    // See https://www.geeksforgeeks.org/orientation-3-ordered-points/
    // for details of below formula.
    int val = (q.y - p.y) * (r.x - q.x) -
              (q.x - p.x) * (r.y - q.y);
 
    if (val == 0) return 0;  // collinear
 
    return (val > 0)? 1: 2; // clock or counterclock wise
}
 
// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Segment s1, Segment s2)
{
    Point p1 = s1.left, q1 = s1.right, p2 = s2.left, q2 = s2.right;
 
    // Find the four orientations needed for general and
    // special cases
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);
 
    // General case
    if (o1 != o2 && o3 != o4)
        return true;
 
    // Special Cases
    // p1, q1 and p2 are collinear and p2 lies on segment p1q1
    if (o1 == 0 && onSegment(p1, p2, q1)) return true;
 
    // p1, q1 and q2 are collinear and q2 lies on segment p1q1
    if (o2 == 0 && onSegment(p1, q2, q1)) return true;
 
    // p2, q2 and p1 are collinear and p1 lies on segment p2q2
    if (o3 == 0 && onSegment(p2, p1, q2)) return true;
 
     // p2, q2 and q1 are collinear and q1 lies on segment p2q2
    if (o4 == 0 && onSegment(p2, q1, q2)) return true;
 
    return false; // Doesn't fall in any of the above cases
}
 
 
// Find predecessor of iterator in s.
 set<Event>::iterator pred(set<Event> &s, set<Event>::iterator it) {
    return it == s.begin() ? s.end() : --it;
}
 
// Find successor of iterator in s.
set<Event>::iterator succ(set<Event> &s, set<Event>::iterator it) {
    return ++it;
}
 
// Returns true if any two lines intersect.
int isIntersect(Segment arr[], int n)
{
    unordered_map<string,int> mp;  // to note the pair for which intersection is checked already
    // Pushing all points to a vector of events
    vector<Event> e;
    for (int i = 0; i < n; ++i) {
        e.push_back(Event(arr[i].left.x, arr[i].left.y, true, i));
        e.push_back(Event(arr[i].right.x, arr[i].right.y, false, i));
    }
 
    // Sorting all events according to x coordinate.
    sort(e.begin(), e.end(), [](Event &e1, Event &e2) {return e1.x < e2.x;});
 
    // For storing active segments.
    set<Event> s;
     int ans=0;
    // Traversing through sorted points
    for (int i=0; i<2*n; i++)
    {
        Event curr = e[i];
        int index = curr.index;
 
        // If current point is left of its segment
        if (curr.isLeft)
        {
            // Get above and below points
            auto next = s.lower_bound(curr);
            auto prev = pred(s, next);
            // Check if current point intersects with
            // any of its adjacent
            bool flag=false;
            if (next != s.end() && doIntersect(arr[next->index], arr[index])){
                string s=to_string(next->index+1)+" "+to_string(index+1);
                if(mp.count(s)==0){mp[s]++;ans++;} //if not already checked we can increase count in map
            }
            if (prev != s.end() && doIntersect(arr[prev->index], arr[index])){
                    string s=to_string(prev->index+1)+" "+to_string(index+1);
                if(mp.count(s)==0){mp[s]++;ans++;} //if not already checked we can increase count in map
            }
            // if same line segment is there then decrease answer as it got increased twice
            if(prev != s.end() && next != s.end() && next->index==prev->index)ans--;
 
 
            // Insert current point (or event)
            s.insert(curr);
        }
 
        // If current point is right of its segment
        else
        {
            // Find the iterator
            auto it=s.find(Event(arr[index].left.x, arr[index].left.y, true, index));
            // Find above and below points
            auto next = succ(s, it);
            auto prev = pred(s, it);
 
            // If above and below point intersect
            if (next != s.end() && prev != s.end())
               {  string s=to_string(next->index+1)+" "+to_string(prev->index+1);
                    string s1=to_string(prev->index+1)+" "+to_string(next->index+1);
                   if (mp.count(s)==0&&mp.count(s1)==0&&doIntersect(arr[prev->index], arr[next->index]))
                    ans++;
                    mp[s]++;
                  }
 
            // Remove current segment
            s.erase(it);
 
        }
    }
    //print pair of lines having intersection
 
    for(auto &pr:mp){
        cout<<"Line: "<<pr.first<<"\n";
    }
    return ans;
}
 
// Driver code
int main() {
    Segment arr[] = { {{1, 5}, {4, 5}}, {{2, 5}, {10, 1}},{{3, 2}, {10, 3}},{{6, 4}, {9, 4}},{{7, 1}, {8, 1}}};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout<<"Number of intersection points: "<<isIntersect(arr, n);
    return 0;
}
Producción

Line: 2 3
Line: 1 2
Number of intersection points: 2

Complejidad del tiempo: el primer paso es la clasificación, que requiere tiempo O (nLogn). El segundo paso procesa 2n puntos y para procesar cada punto, toma tiempo O (Iniciar sesión). Por lo tanto, la complejidad temporal general es O(nLogn) 
Referencias:  
http://www.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf  
http://courses.csail.mit.edu/ 6.006/spring11/lectures/lec24.pdf  
http://www.youtube.com/watch?v=dePDHVovJlE  
http://www.eecs.wsu.edu/~cook/aa/lectures/l25/node10.html 
Escriba comentarios si encuentra algo incorrecto, o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *