【问题标题】:What is an Efficient algorithm to find Area of Overlapping Rectangles什么是找到重叠矩形区域的有效算法
【发布时间】:2010-09-19 15:45:13
【问题描述】:

我的情况

  • 输入:一组矩形
  • 每个矩形由 4 个双精度数组成,如下所示:(x0,y0,x1,y1)
  • 它们不会以任何角度“旋转”,它们都是相对于屏幕“上/下”和“左/右”的“正常”矩形
  • 它们是随机放置的 - 它们可能在边缘接触、重叠或没有任何接触
  • 我将有数百个矩形
  • 这是用 C# 实现的

我需要找到

  • 由它们重叠形成的区域 - 画布中多个矩形“覆盖”的所有区域(例如,两个矩形的交叉点)
  • 我不需要重叠的几何形状 - 只需要面积(例如:4 平方英寸)
  • 不应多次计算重叠 - 例如,假设 3 个具有相同大小和位置的矩形 - 它们彼此重叠 - 该区域应计算一次(而不是 3 次)

示例

  • 下图包含三个矩形:A、B、C
  • A 和 B 重叠(如虚线所示)
  • B 和 C 重叠(如虚线所示)
  • 我正在寻找的是显示破折号的区域

-

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA--------------BBB
AAAAAAAAAAAAAAAA--------------BBB
AAAAAAAAAAAAAAAA--------------BBB
AAAAAAAAAAAAAAAA--------------BBB
                BBBBBBBBBBBBBBBBB
                BBBBBBBBBBBBBBBBB
                BBBBBBBBBBBBBBBBB
                BBBBBB-----------CCCCCCCC
                BBBBBB-----------CCCCCCCC
                BBBBBB-----------CCCCCCCC
                      CCCCCCCCCCCCCCCCCCC
                      CCCCCCCCCCCCCCCCCCC
                      CCCCCCCCCCCCCCCCCCC
                      CCCCCCCCCCCCCCCCCCC

【问题讨论】:

  • 如果它们是随机放置的,那么您可能会有两个不同的重叠区域。你想要所有重叠的总和还是什么?
  • 你能澄清一下吗?对于具有三个矩形 A (0, 0, 10, 10), B (1, 1, 9, 9) 和 C (2, 2, 8, 8) 的场景的答案是什么——即 C 完全在 B 里面,哪个完全在 A 里面?
  • @vitule: 我想说答案是所有 3 个重叠的区域 => C 的区域
  • 请回答第一条评论,以便我们知道需要解决什么问题。
  • @Lance:重读问题后,我认为他想要“完全重叠”的区域。这意味着将有一个或没有重叠区域,这是他正在寻找的。​​span>

标签: algorithm optimization geometry performance


【解决方案1】:

post by user3048546 在第 12-17 行的逻辑中包含错误。这是一个有效的实现:

int rectoverlap (int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2)
{
    int width, height, area;

    if (ax2<bx1 || ay2<by1 || ax1>bx2 || ay1>by2) {
        cout << "Rectangles are not overlapped" << endl;
        return 0;
    }

    if (ax2>=bx2 && bx1>=ax1){
        width=bx2-bx1;
    } else if (bx2>=ax2 && ax1>=bx1) {
        width=ax2-ax1;
    } else if (ax2>bx2) {
        width=bx2-ax1;
    } else {
        width=ax2-bx1;
    }

    if (ay2>=by2 && by1>=ay1){
        height=by2-by1;
    } else if (by2>=ay2 && ay1>=by1) {
        height=ay2-ay1;
    } else if (ay2>by2) {
        height=by2-ay1;
    } else {
        height=ay2-by1;
    }

    area = heigh*width;
    return (area);
}

【讨论】:

    【解决方案2】:

    以下答案应该只给出一次总面积。 它来自以前的答案,但现在在 C# 中实现。 它也适用于浮点数(或双精度,如果需要[它不会遍历 VALUES)。

    学分: http://codercareer.blogspot.co.il/2011/12/no-27-area-of-rectangles.html

    编辑: OP 要求重叠区域 - 这显然非常简单:

    var totArea = rects.Sum(x => x.Width * x.Height);
    

    然后答案是:

    var overlappingArea =totArea-GetArea(rects)
    

    代码如下:

       #region rectangle overlapping
            /// <summary>
            /// see algorithm for detecting overlapping areas here: https://stackoverflow.com/a/245245/3225391
            /// or easier here:
            /// http://codercareer.blogspot.co.il/2011/12/no-27-area-of-rectangles.html
            /// </summary>
            /// <param name="dim"></param>
            /// <returns></returns>
            public static float GetArea(RectangleF[] rects)
            {
                List<float> xs = new List<float>();
                foreach (var item in rects)
                {
                    xs.Add(item.X);
                    xs.Add(item.Right);
                }
                xs = xs.OrderBy(x => x).Cast<float>().ToList();
                rects = rects.OrderBy(rec => rec.X).Cast<RectangleF>().ToArray();
                float area = 0f;
                for (int i = 0; i < xs.Count - 1; i++)
                {
                    if (xs[i] == xs[i + 1])//not duplicate
                        continue;
                    int j = 0;
                    while (rects[j].Right < xs[i])
                        j++;
                    List<Range> rangesOfY = new List<Range>();
                    var rangeX = new Range(xs[i], xs[i + 1]);
                    GetRangesOfY(rects, j, rangeX, out rangesOfY);
                    area += GetRectArea(rangeX, rangesOfY);
                }
                return area;
            }
    
            private static void GetRangesOfY(RectangleF[] rects, int rectIdx, Range rangeX, out List<Range> rangesOfY)
            {
                rangesOfY = new List<Range>();
                for (int j = rectIdx; j < rects.Length; j++)
                {
                    if (rangeX.less < rects[j].Right && rangeX.greater > rects[j].Left)
                    {
                        rangesOfY = Range.AddRange(rangesOfY, new Range(rects[j].Top, rects[j].Bottom));
    #if DEBUG
                        Range rectXRange = new Range(rects[j].Left, rects[j].Right);
    #endif
                    }
                }
            }
    
            static float GetRectArea(Range rangeX, List<Range> rangesOfY)
            {
                float width = rangeX.greater - rangeX.less,
                    area = 0;
    
                foreach (var item in rangesOfY)
                {
                    float height = item.greater - item.less;
                    area += width * height;
                }
                return area;
            }
    
            internal class Range
            {
                internal static List<Range> AddRange(List<Range> lst, Range rng2add)
                {
                    if (lst.isNullOrEmpty())
                    {
                        return new List<Range>() { rng2add };
                    }
    
                    for (int i = lst.Count - 1; i >= 0; i--)
                    {
                        var item = lst[i];
                        if (item.IsOverlapping(rng2add))
                        {
                            rng2add.Merge(item);
                            lst.Remove(item);
                        }
                    }
                    lst.Add(rng2add);
                    return lst;
                }
                internal float greater, less;
                public override string ToString()
                {
                    return $"ln{less} gtn{greater}";
                }
    
                internal Range(float less, float greater)
                {
                    this.less = less;
                    this.greater = greater;
                }
    
                private void Merge(Range rng2add)
                {
                    this.less = Math.Min(rng2add.less, this.less);
                    this.greater = Math.Max(rng2add.greater, this.greater);
                }
                private bool IsOverlapping(Range rng2add)
                {
                    return !(less > rng2add.greater || rng2add.less > greater);
                    //return
                    //    this.greater < rng2add.greater && this.greater > rng2add.less
                    //    || this.less > rng2add.less && this.less < rng2add.greater
    
                    //    || rng2add.greater < this.greater && rng2add.greater > this.less
                    //    || rng2add.less > this.less && rng2add.less < this.greater;
                }
            }
            #endregion rectangle overlapping
    

    【讨论】:

      【解决方案3】:

      考虑到我们有两个矩形(A 和 B),我们有它们的左下角 (x1,y1) 和右上角 (x2,y2) 坐标。使用下面的一段代码可以计算C++中的重叠区域。

          #include <iostream>
      using namespace std;
      
      int rectoverlap (int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2)
      {
          int width, heigh, area;
      
          if (ax2<bx1 || ay2<by1 || ax1>bx2 || ay1>by2) {
              cout << "Rectangles are not overlapped" << endl;
              return 0;
          }
          if (ax2>=bx2 && bx1>=ax1){
              width=bx2-bx1;
              heigh=by2-by1;
          } else if (bx2>=ax2 && ax1>=bx1) {
              width=ax2-ax1;
              heigh=ay2-ay1;
          } else {
              if (ax2>bx2){
                  width=bx2-ax1;
              } else {
                  width=ax2-bx1;
              }
              if (ay2>by2){
                  heigh=by2-ay1;
              } else {
                  heigh=ay2-by1;
              }
          }
          area= heigh*width;
          return (area);
      }
      
      int main()
      {
          int ax1,ay1,ax2,ay2,bx1,by1,bx2,by2;
          cout << "Inter the x value for bottom left for rectangle A" << endl;
          cin >> ax1;
          cout << "Inter the y value for bottom left for rectangle A" << endl;
          cin >> ay1;
          cout << "Inter the x value for top right for rectangle A" << endl;
          cin >> ax2;
          cout << "Inter the y value for top right for rectangle A" << endl;
          cin >> ay2;
          cout << "Inter the x value for bottom left for rectangle B" << endl;
          cin >> bx1;
          cout << "Inter the y value for bottom left for rectangle B" << endl;
          cin >> by1;
          cout << "Inter the x value for top right for rectangle B" << endl;
          cin >> bx2;
          cout << "Inter the y value for top right for rectangle B" << endl;
          cin >> by2;
          cout << "The overlapped area is " <<  rectoverlap (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) << endl;
      }
      

      【讨论】:

        【解决方案4】:

        链接http://codercareer.blogspot.com/2011/12/no-27-area-of-rectangles.html 中列出了一个解决方案,用于查找多个矩形的总面积,这样重叠区域只计算一次。

        上面的解决方案可以扩展为只计算重叠区域(即使重叠区域被多个矩形覆盖,也只计算一次),每对连续的垂直扫描线都有水平扫描线。

        如果目标只是找出所有矩形覆盖的总面积,则不需要水平扫描线,只需将两条垂直扫描线之间的所有矩形合并即可给出该区域。

        另一方面,如果您只想计算重叠区域,则需要水平扫描线来找出垂直(y1,y2)扫描线之间重叠的矩形数量。

        这是我用 Java 实现的解决方案的工作代码。

        import java.io.*;
        import java.util.*;
        
        class Solution {
        
        static class Rectangle{
                 int x;
                 int y;
                 int dx;
                 int dy;
        
                 Rectangle(int x, int y, int dx, int dy){
                   this.x = x;
                   this.y = y;
                   this.dx = dx;
                   this.dy = dy;
                 }
        
                 Range getBottomLeft(){
                    return new Range(x, y);
                 }
        
                 Range getTopRight(){
                    return new Range(x + dx, y + dy);
                 }
        
                 @Override
                 public int hashCode(){
                    return (x+y+dx+dy)/4;
                 }
        
                 @Override
                 public boolean equals(Object other){
                    Rectangle o = (Rectangle) other;
                    return o.x == this.x && o.y == this.y && o.dx == this.dx && o.dy == this.dy;
                 }
        
                @Override
                public String toString(){
                    return String.format("X = %d, Y = %d, dx : %d, dy : %d", x, y, dx, dy);
                }
             }     
        
             static class RW{
                 Rectangle r;
                 boolean start;
        
                 RW (Rectangle r, boolean start){
                   this.r = r;
                   this.start = start;
                 }
        
                 @Override
                 public int hashCode(){
                     return r.hashCode() + (start ? 1 : 0);
                 }
        
                 @Override
                 public boolean equals(Object other){
                      RW o = (RW)other;
                     return o.start == this.start && o.r.equals(this.r);
                 }
        
                @Override
                public String toString(){
                    return "Rectangle : " + r.toString() + ", start = " + this.start;
                }
             }
        
             static class Range{
                 int l;
                 int u;   
        
               public Range(int l, int u){
                 this.l = l;
                 this.u = u;
               }
        
                 @Override
                 public int hashCode(){
                    return (l+u)/2;
                 }
        
                 @Override
                 public boolean equals(Object other){
                    Range o = (Range) other;
                    return o.l == this.l && o.u == this.u;
                 }
        
                @Override
                public String toString(){
                    return String.format("L = %d, U = %d", l, u);
                }
             }
        
             static class XComp implements Comparator<RW>{
                     @Override
                     public int compare(RW rw1, RW rw2){
                         //TODO : revisit these values.
                         Integer x1 = -1;
                         Integer x2 = -1;
        
                         if(rw1.start){
                             x1 = rw1.r.x;
                         }else{
                             x1 = rw1.r.x + rw1.r.dx;
                         }   
        
                         if(rw2.start){
                             x2 = rw2.r.x;
                         }else{
                             x2 = rw2.r.x + rw2.r.dx;
                         }
        
                         return x1.compareTo(x2);
                     }
             }
        
             static class YComp implements Comparator<RW>{
                     @Override
                     public int compare(RW rw1, RW rw2){
                         //TODO : revisit these values.
                         Integer y1 = -1;
                         Integer y2 = -1;
        
                         if(rw1.start){
                             y1 = rw1.r.y;
                         }else{
                             y1 = rw1.r.y + rw1.r.dy;
                         }   
        
                         if(rw2.start){
                             y2 = rw2.r.y;
                         }else{
                             y2 = rw2.r.y + rw2.r.dy;
                         }
        
                         return y1.compareTo(y2);
                     }
             }
        
             public static void main(String []args){
                 Rectangle [] rects = new Rectangle[4];
        
                 rects[0] = new Rectangle(10, 10, 10, 10);
                 rects[1] = new Rectangle(15, 10, 10, 10);
                 rects[2] = new Rectangle(20, 10, 10, 10);
                 rects[3] = new Rectangle(25, 10, 10, 10);
        
                 int totalArea = getArea(rects, false);
                 System.out.println("Total Area : " + totalArea);
        
                 int overlapArea = getArea(rects, true);              
                 System.out.println("Overlap Area : " + overlapArea);
             }
        
        
             static int getArea(Rectangle []rects, boolean overlapOrTotal){
                 printArr(rects);
        
                 // step 1: create two wrappers for every rectangle
                 RW []rws = getWrappers(rects);       
        
                 printArr(rws);        
        
                 // steps 2 : sort rectangles by their x-coordinates
                 Arrays.sort(rws, new XComp());   
        
                 printArr(rws);        
        
                 // step 3 : group the rectangles in every range.
                 Map<Range, List<Rectangle>> rangeGroups = groupRects(rws, true);
        
                 for(Range xrange : rangeGroups.keySet()){
                     List<Rectangle> xRangeRects = rangeGroups.get(xrange);
                     System.out.println("Range : " + xrange);
                     System.out.println("Rectangles : ");
                     for(Rectangle rectx : xRangeRects){
                        System.out.println("\t" + rectx);               
                     }
                 }   
        
                 // step 4 : iterate through each of the pairs and their rectangles
        
                 int sum = 0;
                 for(Range range : rangeGroups.keySet()){
                     List<Rectangle> rangeRects = rangeGroups.get(range);
                     sum += getOverlapOrTotalArea(rangeRects, range, overlapOrTotal);
                 }
                 return sum;         
             }    
        
             static Map<Range, List<Rectangle>> groupRects(RW []rws, boolean isX){
                 //group the rws with either x or y coordinates.
        
                 Map<Range, List<Rectangle>> rangeGroups = new HashMap<Range, List<Rectangle>>();
        
                 List<Rectangle> rangeRects = new ArrayList<Rectangle>();            
        
                 int i=0;
                 int prev = Integer.MAX_VALUE;
        
                 while(i < rws.length){
                     int curr = isX ? (rws[i].start ? rws[i].r.x : rws[i].r.x + rws[i].r.dx): (rws[i].start ? rws[i].r.y : rws[i].r.y + rws[i].r.dy);
        
                     if(prev < curr){
                        Range nRange = new Range(prev, curr);
                        rangeGroups.put(nRange, rangeRects);
                        rangeRects = new ArrayList<Rectangle>(rangeRects);
                     }
                     prev = curr;
        
                     if(rws[i].start){
                       rangeRects.add(rws[i].r);
                     }else{
                       rangeRects.remove(rws[i].r);
                     }
        
                   i++;
                 }
               return rangeGroups;
             }
        
             static int getOverlapOrTotalArea(List<Rectangle> rangeRects, Range range, boolean isOverlap){
                 //create horizontal sweep lines similar to vertical ones created above
        
                 // Step 1 : create wrappers again
                 RW []rws = getWrappers(rangeRects);
        
                 // steps 2 : sort rectangles by their y-coordinates
                 Arrays.sort(rws, new YComp());
        
                 // step 3 : group the rectangles in every range.
                 Map<Range, List<Rectangle>> yRangeGroups = groupRects(rws, false);
        
                 //step 4 : for every range if there are more than one rectangles then computer their area only once.
        
                 int sum = 0;
                 for(Range yRange : yRangeGroups.keySet()){
                     List<Rectangle> yRangeRects = yRangeGroups.get(yRange);
        
                     if(isOverlap){
                         if(yRangeRects.size() > 1){
                             sum += getArea(range, yRange);
                         }
                     }else{
                         if(yRangeRects.size() > 0){
                             sum += getArea(range, yRange);
                         }
                     }
                 }         
                 return sum;
             } 
        
            static int getArea(Range r1, Range r2){
              return (r2.u-r2.l)*(r1.u-r1.l);      
            }
        
            static RW[] getWrappers(Rectangle []rects){
                 RW[] wrappers = new RW[rects.length * 2];
        
                 for(int i=0,j=0;i<rects.length;i++, j+=2){
                     wrappers[j] = new RW(rects[i], true); 
                     wrappers[j+1] = new RW(rects[i], false); 
                 }
                 return wrappers;
             }
        
            static RW[] getWrappers(List<Rectangle> rects){
                 RW[] wrappers = new RW[rects.size() * 2];
        
                 for(int i=0,j=0;i<rects.size();i++, j+=2){
                     wrappers[j] = new RW(rects.get(i), true); 
                     wrappers[j+1] = new RW(rects.get(i), false); 
                 }
                 return wrappers;
             }
        
          static void printArr(Object []a){
            for(int i=0; i < a.length;i++){
              System.out.println(a[i]);
            }
            System.out.println();
          }     
        

        【讨论】:

        • 鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。
        • @davejal,我用上下文更新了答案。谢谢。
        【解决方案5】:

        最简单的解决方案

        import numpy as np
        
        A = np.zeros((100, 100))
        B = np.zeros((100, 100))
        
        A[rect1.top : rect1.bottom,  rect1.left : rect1.right] = 1
        B[rect2.top : rect2.bottom,  rect2.left : rect2.right] = 1
        
        area_of_union     = np.sum((A + B) > 0)
        area_of_intersect = np.sum((A + B) > 1)
        

        在这个例子中,我们创建了两个零矩阵,它们是画布的大小。对于每个矩形,用矩形占用空间的矩阵填充其中一个矩阵。然后对矩阵求和。现在sum(A+B &gt; 0) 是联合区域,sum(A+B &gt; 1) 是重叠区域。这个例子可以很容易地推广到多个矩形。

        【讨论】:

          【解决方案6】:

          我找到了与扫描算法不同的解决方案。

          由于您的矩形都是矩形放置的,矩形的水平和垂直线将形成一个矩形不规则网格。您可以在此网格上“绘制”矩形;这意味着,您可以确定将填写网格的哪些字段。由于网格线是由给定矩形的边界形成的,因此该网格中的字段将始终完全为空或由矩形完全填充。

          我必须用 Java 解决这个问题,所以这是我的解决方案:http://pastebin.com/03mss8yf

          此函数计算矩形占据的完整面积。如果您只对“重叠”部分感兴趣,则必须在第 70 行和第 72 行之间扩展代码块。也许您可以使用第二组来存储多次使用的网格字段。第 70 行和第 72 行之间的代码应替换为如下块:

          GridLocation gl = new GridLocation(curX, curY);
          if(usedLocations.contains(gl) && usedLocations2.add(gl)) {
            ret += width*height;
          } else {
            usedLocations.add(gl);
          }
          

          这里的变量usedLocations2与usedLocations的类型相同;它将被建造 在同一点。

          我不太熟悉复杂度计算;所以我不知道这两种解决方案(扫描或我的网格解决方案)中的哪一种会表现/扩展更好。

          【讨论】:

            【解决方案7】:

            这是我为区域扫描算法编写的代码:

            #include <iostream>
            #include <vector>
            
            using namespace std;
            
            
            class Rectangle {
            public:
                int x[2], y[2];
            
                Rectangle(int x1, int y1, int x2, int y2) {
                    x[0] = x1;
                    y[0] = y1;
                    x[1] = x2;
                    y[1] = y2; 
                };
                void print(void) {
                    cout << "Rect: " << x[0] << " " << y[0] << " " << x[1] << " " << y[1] << " " <<endl;
                };
            };
            
            // return the iterator of rec in list
            vector<Rectangle *>::iterator bin_search(vector<Rectangle *> &list, int begin, int end, Rectangle *rec) {
                cout << begin << " " <<end <<endl;
                int mid = (begin+end)/2;
                if (list[mid]->y[0] == rec->y[0]) {
                    if (list[mid]->y[1] == rec->y[1])
                        return list.begin() + mid;
                    else if (list[mid]->y[1] < rec->y[1]) {
                        if (mid == end)
                            return list.begin() + mid+1;
                        return bin_search(list,mid+1,mid,rec);
                    }
                    else {
                        if (mid == begin)
                            return list.begin()+mid;
                        return bin_search(list,begin,mid-1,rec);
                    }
                }
                else if (list[mid]->y[0] < rec->y[0]) {
                    if (mid == end) {
                        return list.begin() + mid+1;
                    }
                    return bin_search(list, mid+1, end, rec);
                }
                else {
                    if (mid == begin) {
                        return list.begin() + mid;
                    }
                    return bin_search(list, begin, mid-1, rec);
                }
            }
            
            // add rect to rects
            void add_rec(Rectangle *rect, vector<Rectangle *> &rects) {
                if (rects.size() == 0) {
                    rects.push_back(rect);
                }
                else {
                    vector<Rectangle *>::iterator it = bin_search(rects, 0, rects.size()-1, rect);
                    rects.insert(it, rect);
                }
            }
            
            // remove rec from rets
            void remove_rec(Rectangle *rect, vector<Rectangle *> &rects) {
                vector<Rectangle *>::iterator it = bin_search(rects, 0, rects.size()-1, rect);
                rects.erase(it);
            }
            
            // calculate the total vertical length covered by rectangles in the active set
            int vert_dist(vector<Rectangle *> as) {
                int n = as.size();
            
                int totallength = 0;
                int start, end;
            
                int i = 0;
                while (i < n) {
                    start = as[i]->y[0];
                    end = as[i]->y[1];
                    while (i < n && as[i]->y[0] <= end) {
                        if (as[i]->y[1] > end) {
                            end = as[i]->y[1];
                        }
                        i++;
                    }
                    totallength += end-start;
                }
                return totallength;
            }
            
            bool mycomp1(Rectangle* a, Rectangle* b) {
                return (a->x[0] < b->x[0]);
            }
            
            bool mycomp2(Rectangle* a, Rectangle* b) {
                return (a->x[1] < b->x[1]);
            }
            
            int findarea(vector<Rectangle *> rects) {
                vector<Rectangle *> start = rects;
                vector<Rectangle *> end = rects;
                sort(start.begin(), start.end(), mycomp1);
                sort(end.begin(), end.end(), mycomp2);
            
                // active set
                vector<Rectangle *> as;
            
                int n = rects.size();
            
                int totalarea = 0;
                int current = start[0]->x[0];
                int next;
                int i = 0, j = 0;
                // big loop
                while (j < n) {
                    cout << "loop---------------"<<endl;
                    // add all recs that start at current
                    while (i < n && start[i]->x[0] == current) {
                        cout << "add" <<endl;
                        // add start[i] to AS
                        add_rec(start[i], as);
                        cout << "after" <<endl;
                        i++;
                    }
                    // remove all recs that end at current
                    while (j < n && end[j]->x[1] == current) {
                        cout << "remove" <<endl;
                        // remove end[j] from AS
                        remove_rec(end[j], as);
                        cout << "after" <<endl;
                        j++;
                    }
            
                    // find next event x
                    if (i < n && j < n) {
                        if (start[i]->x[0] <= end[j]->x[1]) {
                            next = start[i]->x[0];
                        }
                        else {
                            next = end[j]->x[1];
                        }
                    }
                    else if (j < n) {
                        next = end[j]->x[1];
                    }
            
                    // distance to next event
                    int horiz = next - current;
                    cout << "horiz: " << horiz <<endl;
            
                    // figure out vertical dist
                    int vert = vert_dist(as);
                    cout << "vert: " << vert <<endl;
            
                    totalarea += vert * horiz;
            
                    current = next;
                }
                return totalarea;
            }
            
            int main() {
                vector<Rectangle *> rects;
                rects.push_back(new Rectangle(0,0,1,1));
            
                rects.push_back(new Rectangle(1,0,2,3));
            
                rects.push_back(new Rectangle(0,0,3,3));
            
                rects.push_back(new Rectangle(1,0,5,1));
            
                cout << findarea(rects) <<endl;
            }
            

            【讨论】:

            • 您的答案似乎仅涵盖您可以使用 ++ 遍历“区域”的情况。如果矩形是浮动 - 你的解决方案没有帮助..我是对的吗?
            【解决方案8】:

            使用示例:

             1 2 3 4 5 6
            
            1 +---+---+
               | |
            2 + A +---+---+
               | |乙|
            3 + + +---+---+
               | | | | |
            4 +---+---+---+---+ +
                           | |
            5 + C +
                           | |
            6 +---+---+
            

            1)将所有x坐标(左右)收集到一个列表中,然后对其进行排序并删除重复项

            1 3 4 5 6

            2) 将所有 y 坐标(顶部和底部)收集到一个列表中,然后对其进行排序并删除重复项

            1 2 3 4 6

            3) 根据唯一 x 坐标之间的间隙数 * 唯一 y 坐标之间的间隙数创建一个二维数组。

            4 * 4

            4) 将所有矩形绘制到这个网格中,增加它出现的每个单元格的计数:

            1 3 4 5 6 1 +---+ | 1 | 0 0 0 2 +---+---+---+ | 1 | 1 | 1 | 0 3 +---+---+---+---+ | 1 | 1 | 2 | 1 | 4 +---+---+---+---+ 0 0 | 1 | 1 | 6 +---+---+

            5) 网格中计数大于 1 的单元格的总面积为重叠面积。为了在稀疏用例中提高效率,您实际上可以在绘制矩形时保持该区域的总和,每次将单元格从 1 移动到 2 时。


            在问题中,矩形被描述为四个双精度。双精度数通常包含舍入误差,并且误差可能会蔓延到您计算的重叠区域。如果合法坐标位于有限点,请考虑使用整数表示。


            如果分辨率可以接受,PS 在我的其他答案中使用硬件加速器并不是一个破旧的想法。它也很容易用比我上面概述的方法少得多的代码来实现。课程用马。

            【讨论】:

            • 这是一个O(n^3) 算法
            【解决方案9】:

            计算该区域的一种有效方法是使用扫描算法。让我们假设我们通过矩形 U 的并集扫过一条垂直线 L(x):

            • 首先,您需要构建一个事件队列 Q,在本例中,它是矩形的所有 x 坐标(左和右)的有序列表。
            • 在扫描期间,您应该维护一个 1D 数据结构,它应该为您提供 L(x) 和 U 的交集的总长度。重要的是这个长度在两个连续事件 q 和 q' 之间是恒定的Q. 所以,如果 l(q) 表示 L(q+) 的总长度(即 L 恰好在 q 的右侧)与 U 相交,则 L 在事件 q 和 q' 之间扫过的区域正好是 l(q)* (q' - q)。
            • 您只需将所有这些扫过的区域相加即可得出总和。

            我们仍然需要解决一维问题。您需要一个 1D 结构,它动态计算(垂直)段的联合。动态地,我的意思是您有时会添加一个新段,有时会删除一个。

            我已经在我对collapsing ranges question 的回答中详细说明了如何以静态方式进行操作(实际上是一维扫描)。所以如果你想要一些简单的东西,你可以直接应用它(通过重新计算每个事件的联合)。如果你想要更高效的东西,你只需要稍微调整一下:

            • 假设您知道段 S1...Sn 的并集由不相交的段 D1...Dk。添加 Sn+1 非常简单,只需将 Sn+1 的两端定位到 D1 的两端即可。 .Dk.
            • 假设您知道段 S1...Sn 的并集由不相交的段 D1...Dk,去掉segment Si(假设Si包含在Dj中)意味着重新计算segments的并集Dj 由 Si 组成(使用静态算法)。

            这是您的动态算法。假设您将使用带有日志时间位置查询的排序集来表示 D1...Dk,这可能是您可以获得的最有效的非专业方法.

            【讨论】:

            • 是的,但是在从集合中删除段 S 后,您必须进行 重新计算,在最坏的情况下为 O(n)。所以总运行时间 O(n^2),不是吗?想象一下这种情况:bit.ly/8Zd5jO
            • @Martin:除非你使用像区间树这样的数据结构。那会给你O(nlogn)
            • @ThomasAhle:你能解释一下如何使用区间树吗?我知道您可以在 O(log n + m) 中查询与给定间隔重叠的间隔树,但是如何找到 O(log n + m) 中的间隔的并集?
            • @extraeee 所以我们将问题简化为动态一维联合区域。您必须稍微聪明一点,将部分结果存储在树的节点上。添加或删除间隔时,您只需更改登录节点。
            【解决方案10】:

            您可以找到 x 轴和 y 轴上的重叠并将它们相乘。

            int LineOverlap(int line1a, line1b, line2a, line2b) 
            {
              // assume line1a <= line1b and line2a <= line2b
              if (line1a < line2a) 
              {
                if (line1b > line2b)
                  return line2b-line2a;
                else if (line1b > line2a)
                  return line1b-line2a;
                else 
                  return 0;
              }
              else if (line2a < line1b)
                return line2b-line1a;
              else 
                return 0;
            }
            
            
            int RectangleOverlap(Rect rectA, rectB) 
            {
              return LineOverlap(rectA.x1, rectA.x2, rectB.x1, rectB.x2) *
                LineOverlap(rectA.y1, rectA.y2, rectB.y1, rectB.y2);
            }
            

            【讨论】:

            • 编辑:我使用 int 但你可以用双精度替换它们。
            • 它适用于 2 个矩形。如果您有 3 个共享重叠区域怎么办?更糟糕的是……如果你有 200 万个矩形呢?
            • 谢谢,我可以看到这对两个矩形有什么帮助,但我不清楚如何解释例如一百个输入矩形,这些矩形可能在许多组合中重叠。
            • 您可以创建 rect1 和 2 的重叠矩形并将其与 3 一起使用,等等,直到所有矩形都通过。
            • 糟糕,如果我读到“它们没有以任何角度“旋转”的问题,它们都是相对于“上/下”和“左/右”的“正常”矩形屏幕”
            【解决方案11】:

            如果你将每个矩形分割成更小的矩形,你可以大大简化这个问题。收集所有矩形的所有 X 和 Y 坐标,这些将成为您的分割点 - 如果一个矩形穿过线,则将其一分为二。完成后,您会得到一个重叠 0% 或 100% 的矩形列表,如果您对它们进行排序,应该很容易找到相同的矩形。

            【讨论】:

            • 这是扫描线(或扫描线)算法的开始,而这又是广义布尔合并包的开始。根据这些术语进行一些谷歌搜索,您会发现在沉重的矩形负载下可以优雅地扩展。
            • 这个解决方案可以生成许多二次方矩形:想象许多细长的水平矩形与许多细长的垂直矩形交叉。这个问题有一个 O(n*log(n)) 的解决方案。
            【解决方案12】:

            这是我脑海中的一些东西,听起来可能会奏效:

            1. 用一个双键和一个矩形+布尔值列表创建一个字典,像这样:

              字典>> 矩形;

            2. 对于集合中的每个矩形,找到 x0 和 x1 值的对应列表,并将矩形添加到该列表中,对于 x0,布尔值为 true,对于 x1,布尔值为 false。这样,您现在就有了每个矩形进入(真)或离开(假)x 方向的所有 x 坐标的完整列表

            3. 从该字典中获取所有键(所有不同的 x 坐标),对它们进行排序,并按顺序遍历它们,确保您可以同时获得当前 x 值和下一个 x 值好吧(你需要他们两个)。这为您提供了单独的矩形条

            4. 维护一组您当前正在查看的矩形,它们一开始是空的。对于您在第 3 点中迭代的每个 x 值,如果矩形注册为真值,则将其添加到集合中,否则将其删除。
            5. 对于条形,按矩形的 y 坐标对矩形进行排序
            6. 遍历条带中的矩形,计算重叠距离(我还不清楚如何有效地做到这一点)
            7. 计算条带宽度乘以重叠距离的高度以获得面积

            例如,5 个矩形,相互重叠绘制,从 a 到 e:

            aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
            aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
            aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
            aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
            aaaaaaaadddddddddddddddddddddddddddddbbbbbb
            aaaaaaaadddddddddddddddddddddddddddddbbbbbb
                    ddddddddddddddddddddddddddddd
                    ddddddddddddddddddddddddddddd
                    ddddddddddddddeeeeeeeeeeeeeeeeee
                    ddddddddddddddeeeeeeeeeeeeeeeeee
                    ddddddddddddddeeeeeeeeeeeeeeeeee
            ccccccccddddddddddddddeeeeeeeeeeeeeeeeee
            ccccccccddddddddddddddeeeeeeeeeeeeeeeeee
            cccccccccccc          eeeeeeeeeeeeeeeeee
            cccccccccccc          eeeeeeeeeeeeeeeeee
            cccccccccccc
            cccccccccccc
            

            这是 x 坐标列表:

            v       v  v   v      v   v         v  v  v   
            |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
            |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
            |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
            |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
            |aaaaaaaddd|dddddddddd|ddddddddddddddbb|bbb
            |aaaaaaaddd|dddddddddd|ddddddddddddddbb|bbb
            |       ddd|dddddddddd|dddddddddddddd  |
            |       ddd|dddddddddd|dddddddddddddd  |
            |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
            |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
            |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
            ccccccccddd|ddddddddddeeeeeeeeeeeeeeeeee
            ccccccccddd|ddddddddddeeeeeeeeeeeeeeeeee
            cccccccccccc          eeeeeeeeeeeeeeeeee
            cccccccccccc          eeeeeeeeeeeeeeeeee
            cccccccccccc
            cccccccccccc
            

            列表将是(其中每个 v 都被简单地赋予一个从 0 开始向上的坐标):

            0: +a, +c
            1: +d
            2: -c
            3: -a
            4: +e
            5: +b
            6: -d
            7: -e
            8: -b
            

            因此,每个条带将是(从上到下排序的矩形):

            0-1: a, c
            1-2: a, d, c
            2-3: a, d
            3-4: d
            4-5: d, e
            5-6: b, d, e
            6-7: b, e
            7-8: b
            

            对于每个条带,重叠将是:

            0-1: none
            1-2: a/d, d/c
            2-3: a/d
            3-4: none
            4-5: d/e
            5-6: b/d, d/e
            6-7: none
            7-8: none
            

            我想对于上下检查的排序 + 进入/离开算法的变体也是可行的:

            1. 对我们当前在条带中分析的矩形进行排序,从上到下,对于具有相同上坐标的矩形,也按下坐标排序
            2. 遍历 y 坐标,当你输入一个矩形时,将它添加到集合中,当你离开一个矩形时,将它从集合中移除
            3. 只要集合有多个矩形,就会出现重叠(如果您确保添加/删除所有具有相同顶部/底部坐标的矩形,则多个重叠矩形不会成为问题

            对于上面的 1-2 条,您可以这样迭代:

            0. empty set, zero sum
            1. enter a, add a to set (1 rectangle in set)
            2. enter d, add d to set (>1 rectangles in set = overlap, store this y-coordinate)
            3. leave a, remove a from set (now back from >1 rectangles in set, add to sum: y - stored_y
            4. enter c, add c to set (>1 rectangles in set = overlap, store this y-coordinate)
            5. leave d, remove d from set (now back from >1 rectangles in set, add to sum: y - stored_y)
            6. multiply sum with width of strip to get overlapping areas
            

            您实际上也不必在这里维护一个实际的集合,只需计算您所在的矩形的数量,每当它从 1 变为 2 时,存储 y,每当它从 2 变为 1 时,计算当前 y - 存储的 y,并将这个差值相加。

            希望这是可以理解的,正如我所说,这不是我的想法,没有经过任何测试。

            【讨论】:

              【解决方案13】:

              这种类型的碰撞检测通常称为 AABB(Axis Aligned Bounding Boxes),这是 google search 的一个很好的起点。

              【讨论】:

              • 这个问题不是碰撞检测(查找矩形是否与矩形集重叠),而是计算它们的联合区域。对于重叠问题,您可以使用扫描线算法和间隔树(算法简介书)来测试 O(log(n)) 中的(间隔)-(间隔集)碰撞。
              【解决方案14】:

              如果您的矩形将是稀疏的(大部分不相交),那么可能值得一看递归维度聚类。否则,四叉树似乎是要走的路(正如其他海报所提到的那样。

              这是计算机游戏中碰撞检测中的常见问题,因此不乏建议解决方法的资源。

              Here 是一篇很好的总结 RCD 的博文。

              Here是Dr.Dobbs的一篇文章,总结了各种碰撞检测算法,应该是合适的。

              【讨论】:

                【解决方案15】:

                这是我在 TopCoder SRM 160 Div 2 中使用的一些快速而肮脏的代码。

                t = 顶部
                b = 底部
                l = 左
                r = 对

                public class Rect
                {
                    public int t, b, l, r;
                
                    public Rect(int _l, int _b, int _r, int _t)
                    {
                        t = _t;
                        b = _b;
                        l = _l;
                        r = _r;
                    }   
                
                    public bool Intersects(Rect R)
                    {
                        return !(l > R.r || R.l > r || R.b > t || b > R.t);
                    }
                
                    public Rect Intersection(Rect R)
                    {
                        if(!this.Intersects(R))
                            return new Rect(0,0,0,0);
                        int [] horiz = {l, r, R.l, R.r};
                        Array.Sort(horiz);
                        int [] vert = {b, t, R.b, R.t};
                        Array.Sort(vert);
                
                        return new Rect(horiz[1], vert[1], horiz[2], vert[2]);
                    } 
                
                    public int Area()
                    {
                        return (t - b)*(r-l);
                    }
                
                    public override string ToString()
                    {
                        return l + " " + b + " " + r + " " + t;
                    }
                }
                

                【讨论】:

                • 我真的很困惑,除非我阅读了这个问题并发现它将三角形限制为平行于 X 和 Y 轴的轴。知道这很好(虽然选择第二高的可能比排序更快哈哈)。
                【解决方案16】:

                一种解决方法是将其绘制到画布上!使用半透明颜色绘制每个矩形。 .NET 运行时将使用优化的本机代码进行绘图 - 甚至使用硬件加速器。

                然后,您必须回读像素。每个像素是背景颜色、矩形颜色还是其他颜色?它可以是另一种颜色的唯一方法是两个或多个矩形重叠......

                如果这太作弊了,我会像另一个回答者那样推荐四叉树,或者r-tree

                【讨论】:

                • 其实这个想法还不错,很容易解决多个路口。我想这完全取决于您需要的分辨率。
                • 为什么要投反对票?特别是对于大型矩形集和有限的所需分辨率,位图方法的 O(1) 性能胜过精确方法的通常 O(n^2)。说真的,人们......
                • 这不是一个精确的算法,也不是很整洁。它还会促使您使用更大的栅格来获得更好的精度,总体而言,它只是在速度上输给了其他人可能已经实施的扫描算法。 (说真的,扫地很快!)
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2010-12-05
                • 2019-12-12
                • 2016-12-27
                • 2011-09-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多