Count the number of adjacent boxes

前端 未结 3 1333
情书的邮戳
情书的邮戳 2021-01-18 06:46

Suppose I have a set of (X,Y) coordinates of 1000 boxes.

         (    x1,    y1)    (    x2,    y2)      Area

         (0.0000,0.0000)    (0.3412,0.4175)           


        
相关标签:
3条回答
  • 2021-01-18 07:09

    A naive solution requires O(N^2) where N is the number of rectangle, here is how to do it faster.

    Two rectangles are adjacent only if they have one coordinate in common (note that the reverse is not correct). Therefore you can count the number of adjacent boxes faster by first partitioning your input rectangles using two hashes, one based on the x location of the rectangle, and the other based on the y location. As a result, one rectangle will fit into four different hash buckets based on its x1, y1, x2, and y2.


    Example

    For example, rect (0.0000,0.0000) (0.3412,0.4175) will be hashed into bucketX(0.000), bucketX(0.3412), bucketY(0.0000), and bucketY(0.4175).

    From the input in the OP, bucketX(0.000) and bucketX(1.000) will have the following rectangles:

    bucketX(0.0000):
       (0.0000,0.0000)    (0.3412,0.4175)
       (0.0000,0.4175)    (0.3412,0.6553)
       (0.0000,0.6553)    (0.7445,1.0000)
       (0.0000,0.4175)    (0.3412,0.6553)
    
    bucketX(1.0000):
       (0.7445,0.0000)    (1.0000,0.6553)
       (0.7445,0.6553)    (1.0000,1.0000)
    

    Time Complexity

    The hashing step only requires O(N) computation time where N is the number of rectangles, and the resulting check requires O(m^2) where m is the size of the largest bucket which in most cases is much smaller than N.


    Checking adjacency within each hashed bucket

    Then, for all rectangles within the same hash bucket. Check whether two rectangles are adjacent by determining whether they either have same x value and overlapped value in y or vice versa.

    The following is an example of checking whether two rectangles are adjacent:

    class Rect {
    public:
      double x1, x2, y1, y2; // assuming x1 <= x2 and y1 <= y2
    
      ...
    
      bool isAdjacent(Rect rect) {
        if (x1 == rect.x1 || x1 == rect.x2 ||
            x2 == rect.x1 || x2 == rect.x2) {
          // use only < when comparing y1 and rect.y2 avoids sharing only a corner
          if (y1 >= rect.y1 && y1 < rect.y2) {
            return true;
          }
          if (y2 > rect.y1 && y2 <= rect.y2) {
            return true;
          }
          if (rect.y1 >= y1 && rect.y1 < y2) {
            return true;
          }
          if (rect.y2 > y1 && rect.y2 <= y2) {
            return true;
          }
        }
        if (y1 == rect.y1 || y1 == rect.y2 ||
            y2 == rect.y1 || y2 == rect.y2) {
          if (x1 >= rect.x1 && x1 < rect.x2) {
            return true;
          }
          if (x2 > rect.x1 && x2 <= rect.x2) {
            return true;
          }
          if (rect.x1 >= x1 && rect.x1 < x2) {
            return true;
          }
          if (rect.x2 > x1 && rect.x2 <= x2) {
            return true;
          }
        }
        return false;
      }
    }
    

    A Runnable Example

    Here provide a sample code for the adjacency check:

    #include <stdio.h>
    #include <stdlib.h>
    #include <vector>
    
    class Rect {
    public:
      double x1, x2, y1, y2; // assuming x1 <= x2 and y1 <= y2
    
      Rect(double X1, double Y1, double X2, double Y2) {
        if (X1 < X2) {
          x1 = X1; x2 = X2;
        } else {
          x2 = X1; x1 = X2;
        }
        if (Y1 < Y2) {
          y1 = Y1; y2 = Y2;
        } else {
          y2 = Y1; y1 = Y2;
        }
      }
    
      double area() {
        return (x2 - x1) * (y2 - y1);
      }
    
      bool isAdjacent(Rect rect) {
        if (x1 == rect.x1 || x1 == rect.x2 ||
            x2 == rect.x1 || x2 == rect.x2) {
          // use only < when comparing y1 and rect.y2 avoids sharing only a corner
          if (y1 >= rect.y1 && y1 < rect.y2) {
            return true;
          }
          if (y2 > rect.y1 && y2 <= rect.y2) {
            return true;
          }
          if (rect.y1 >= y1 && rect.y1 < y2) {
            return true;
          }
          if (rect.y2 > y1 && rect.y2 <= y2) {
            return true;
          }
        }
        if (y1 == rect.y1 || y1 == rect.y2 ||
            y2 == rect.y1 || y2 == rect.y2) {
          if (x1 >= rect.x1 && x1 < rect.x2) {
            return true;
          }
          if (x2 > rect.x1 && x2 <= rect.x2) {
            return true;
          }
          if (rect.x1 >= x1 && rect.x1 < x2) {
            return true;
          }
          if (rect.x2 > x1 && rect.x2 <= x2) {
            return true;
          }
        }
        return false;
      }
    };
    
    int main() {
    
      std::vector<Rect> rects;
    
      rects.push_back(Rect(9999, 9999, 9999, 9999));
      rects.push_back(Rect(0.0000,0.0000, 0.8147,0.1355));
      rects.push_back(Rect(0.8147,0.0000, 1.0000,0.1355));
      rects.push_back(Rect(0.8147,0.1355, 0.9058,0.8350));
      rects.push_back(Rect(0.0000,0.1355, 0.1270,0.9689));
      rects.push_back(Rect(0.9058,0.1355, 0.9134,0.2210));
    
      rects.push_back(Rect(0.9058,0.8350, 1.0000,1.0000));
      rects.push_back(Rect(0.8147,0.8350, 0.9058,1.0000));
      rects.push_back(Rect(0.1270,0.1355, 0.6324,0.3082));
      rects.push_back(Rect(0.1270,0.9689, 0.8147,1.0000));
      rects.push_back(Rect(0.0000,0.9689, 0.1270,1.0000));
    
      rects.push_back(Rect(0.9134,0.1355, 1.0000,0.2210));
      rects.push_back(Rect(0.9134,0.2210, 1.0000,0.8350));
      rects.push_back(Rect(0.9058,0.2210, 0.9134,0.8350));
      rects.push_back(Rect(0.6324,0.1355, 0.8147,0.3082));
      rects.push_back(Rect(0.6324,0.3082, 0.8147,0.9689));
    
      rects.push_back(Rect(0.1270,0.3082, 0.6324,0.9689));
    
    
      int adj_count = 0;
      int y = 1;
      for (int x = 0; x < rects.size(); ++x) {
        if (x == y) continue;
        if (rects[y].isAdjacent(rects[x])) {
          printf("rect[%d] is adjacent with rect[%d]\n", y, x);
        }
      }
      y = 2;
      for (int x = 0; x < rects.size(); ++x) {
        if (x == y) continue;
        if (rects[y].isAdjacent(rects[x])) {
          printf("rect[%d] is adjacent with rect[%d]\n", y, x);
        }
      }
    }
    

    and the output is:

    rect[1] is adjacent with rect[2]
    rect[1] is adjacent with rect[4]
    rect[1] is adjacent with rect[8]
    rect[1] is adjacent with rect[14]
    rect[2] is adjacent with rect[1]
    rect[2] is adjacent with rect[3]
    rect[2] is adjacent with rect[5]
    rect[2] is adjacent with rect[11]
    
    0 讨论(0)
  • 2021-01-18 07:22

    In the scenario you describe boxes share corners, so if you computed all of the box corners then you could see which ones were touching

    // O(n)
    foreach box b {
       // compute b's other 2 corners and save them
    }
    
    // 16 * O(n^2) = O(n^2) 
    foreach box b {
       foreach box other {
          // match if any of b's corners match any of others points
       }
    }
    

    There is probably a much more efficient/elegant solution, this one is somewhat naive.

    0 讨论(0)
  • 2021-01-18 07:30

    Let's say you're interested in box B for which the (x0, y0, x1, y1) coordinates are: (B.x0, B.y0, B.x1, B.y1)

    Then with:

    ax0 = min(x0,x1);
    ax1 = max(x0,x1);
    ay0 = min(y0,y1);
    ay1 = max(y0,y1);
    bx0 = min(B.x0,B.x1);
    bx1 = max(B.x0,B.x1);
    by0 = min(B.y0,B.y1);
    by1 = max(B.y0,B.y1);
    

    you go through the whole list of boxes (with a for loop) and you select the ones for which:

    (((ay0 <= by1 && ay1 >= by0) && (ax1 == bx0 || ax0 == bx1) || // touch on x axis
     ((ax0 <= bx1 && ax1 >= bx0) && (ay1 == by0 || ay0 == by1))   // touch on y axis
    
    0 讨论(0)
提交回复
热议问题