【问题标题】:Combined area of overlapping circles重叠圆的组合面积
【发布时间】:2010-12-12 15:28:09
【问题描述】:

我最近遇到了一个问题,我有四个圆(中点和半径),必须计算这些圆的并集面积。

示例图片:

两个圆圈很容易,

我可以只计算不在三角形内的每个圆圈面积的分数,然后计算三角形的面积。

但是当有两个以上的圆圈时,我可以使用一个聪明的算法吗?

【问题讨论】:

  • 这是一个非常有趣的问题,我记得在高中几何课上看到过这个问题,但一直没有找到解决方案。如果您在此处找不到答案,请尝试将其发布到 mathoverflow.net 并让数学家破解它:P
  • 有时真正的程序员需要真正的数学
  • 如何找出这个问题的答案 - “我们在这 4 个地点都有销售代表,每个人都服务于具有这 4 个半径的区域。我们覆盖了全国的多少? "如果您的销售代表数据库不断变化,这将成为一个编程问题!
  • 其实这是真正的程序员喜欢思考的问题。
  • @zvolkov:电路板是用一种语言来描述的,它可以将正方形和圆圈向下倾斜,并可选择拖动它们。 “计算铜面积”。 (这可能需要计算蚀刻时间,知道是否添加清除艺术品,各种东西。)

标签: algorithm geometry area


【解决方案1】:

我发现这个链接可能有用。不过好像还没有一个确定的答案。 Google answers。三个圆圈的另一个参考是Haruki's theorem。那里也有一篇论文。

【讨论】:

    【解决方案2】:

    我确信有一个聪明的算法,但这里有一个愚蠢的算法,可以省去寻找它;

    • 在圆圈周围放置一个边界框;
    • 在边界框内生成随机点;
    • 判断随机点是否在其中一个圆圈内;
    • 通过一些简单的加法和除法计算面积 (proportion_of_points_inside*area_of_bounding_box)。

    当然,这很愚蠢,但是:

    • 您可以获得尽可能准确的答案,只需获得更多分数;
    • 它适用于您可以计算内部/外部区别的任何形状;
    • 它将完美地并行化,因此您可以使用所有内核。

    【讨论】:

    • 这可行,但像这样的蒙特卡罗方法,仅基于均匀采样,通常没有最佳收敛速度。
    • 抱歉,尽管我很欣赏您的努力并认为您的解决方案“实用”,但我认为您的方法非常错误。这是一个不能而且应该通过数学而不是蛮力来解决的问题。在这样的问题上浪费精力和核心是浪费和奢侈。
    • 你说得对,我为自己感到羞耻,但我有一个拥有 12,000 个核心的集群,我可以负担得起。而且我不知道如何使优雅的数学解决方案扩展到那么多处理器。
    • 蒙特卡洛(或任何随机)方法本质上没有任何问题,只要它提供所需的准确度并在合理的时间内完成。
    • @mafutrct,你当然是对的。然而,在数学上很容易犯一些小错误。此解决方案提供了一种测试正确性的简单方法。
    【解决方案3】:

    我喜欢 2 个相交圆的情况的方法——在更复杂的示例中,我将使用相同方法的轻微变化。

    它可能会更好地了解将算法推广到更多的半重叠圆。

    这里的不同之处在于我从连接中心开始(所以在圆心之间有一个顶点,而不是在圆相交的地方之间)我认为这样可以更好地概括。

    (在实践中,也许蒙特卡罗方法是值得的)


    (来源:secretGeek.net

    【讨论】:

    • 我认为按照您的图像建议进行多边形分割可能是一种非常好的方法。有很多细节需要解决才能对其进行编码。它如何处理一个由 20 个圆圈组成的链,每个圆圈只与链中的最后一个和下一个重叠?用手很容易弄清楚,但你的算法是什么?
    【解决方案4】:

    嗯,很有趣的问题。我的方法可能类似于以下内容:

    • 找出一种方法来计算任意数量的圆之间的相交区域是什么,即如果我有 3 个圆,我需要能够计算出这些圆之间的相交区域是什么。 “蒙特卡洛”方法将是一种近似此方法的好方法 (http://local.wasp.uwa.edu.au/~pbourke/geometry/circlearea/)。
    • 消除任何完全包含在另一个更大圆圈中的圆圈(查看半径和两个圆圈中心之间距离的模数)我认为不是强制性的。
    • 选择 2 个圆圈(分别称为 A 和 B)并使用以下公式计算总面积:

    (对于任何形状都是如此,无论是圆形还是其他)

    area(A∪B) = area(A) + area(B) - area(A∩B)
    

    A ∪ B 表示 A 并集 B,A ∩ B 表示 A 与 B 相交(您可以从第一步开始计算。

    • 现在继续添加圆,并继续计算添加的面积,作为圆面积和圆之间相交面积的总和/减法。例如对于 3 个圆(称为额外的圆 C),我们使用以下公式计算面积:

    (这与上面A已替换为A∪B的情况相同)

    area((A∪B)∪C) = area(A∪B) + area(C) - area((A∪B)∩C)
    

    area(A∪B) 是我们刚刚计算出来的,area((A∪B)∩C) 可以找到:

    area((A∪B)nC) = area((A∩C)∪(B∩C)) = area(A∩C) + area(A∩B) - area((A∩C)∩(B∩C)) = area(A∩C) + area(A∩B) - area(A∩B∩C)
    

    你又可以从上面找到区域(A∩B∩C)。

    棘手的一点是最后一步 - 添加的圆圈越多,它变得越复杂。我相信有一个扩展可以用有限的联合计算出交集的面积,或者你也可以递归地计算出来。

    另外关于使用蒙特卡罗来近似迭代的面积,我相信可以将任意数量的圆的交点减少到其中4个圆的交点,可以精确计算(不知道如何但是要这样做)。

    顺便说一句,可能有更好的方法 - 复杂性显着增加(可能成倍增加,但我不确定)每增加一个额外的圆圈。

    【讨论】:

    • 格式怎么了?也很抱歉将 n 和 u 用于交集和联合,可能有更好的方法......
    • 添加了一些 unicode 联合 (∪) 和交叉 (∩) 标志。希望他们能工作。
    【解决方案5】:

    查找外周上的所有圆交点(例如下图中的 B、D、F、H)。将它们与相应圆的中心连接在一起,形成一个多边形。圆的并集面积是多边形的面积 + 由连续交点和它们之间的圆心定义的圆切片的面积。您还需要考虑任何漏洞。

    【讨论】:

    • 中间有个洞会怎样?
    • 您需要从总数中减去孔的中心连接多边形,并将该多边形的圆形切片添加到总数中。
    • 很好,但我想这需要很多实现细节来处理所有特殊情况(在另一个里面有圆圈,没有交叉点,孔,一点接触......)
    • 特殊情况非常简单。由于没有任何周边交叉点,其他圆圈内的圆圈被丢弃。一点接触实际上是两个零距离的交点。如果中心的距离小于半径的总和,则可以通过连接组件算法在连接两个圆的图形上找到断开的形状。除了面积最大的洞外,洞都是多边形。周界交叉点是所有不严格位于任何圆内的交叉点。
    • 是的,但是孔的边界也是(小)弧线。我仍然认为这需要大量代码才能正常工作。
    【解决方案6】:

    根据您要解决的问题,获得上限和下限可能就足够了。上限很简单,只是所有圆圈的总和。对于下限,您可以选择一个半径,使所有圆都不重叠。为了更好地找到每个圆的最大半径(直到实际半径),使其不重叠。删除任何完全重叠的圆也应该很简单(所有这些圆都满足|P_a - P_b|

    给定一个上限和下限,您也许可以更好地调整蒙特卡罗方法,但没有什么特别的想法。另一种选择(同样取决于您的应用程序)是对圆进行光栅化并计算像素。它基本上是具有固定分布的蒙特卡罗方法。

    【讨论】:

      【解决方案7】:

      对于与前一个不同的解决方案,您可以使用四叉树生成具有任意精度的估计。

      如果您可以判断正方形是在内部还是外部或与形状相交,这也适用于任何形状联合。

      每个单元格都有以下状态之一:空、满、部分

      该算法包括“绘制”四叉树中的圆圈,从低分辨率开始(例如标记为空的 4 个单元格)。每个单元格是:

      • 在至少一个圆圈内,然后将单元格标记为已满,
      • 在所有圆圈之外,将单元格标记为空,
      • 否则将单元格标记为部分。

      完成后,您可以计算面积估计值:完整单元格给出下限,空单元格给出上限,部分单元格给出最大面积误差。

      如果误差对您来说太大,您可以细化部分单元格,直到获得正确的精度。

      我认为这将比可能需要处理许多特殊情况的几何方法更容易实现。

      【讨论】:

      • 我的猜测是,这也将比蒙特卡罗内/外点算法收敛得更快。
      • 这似乎更容易实现。绝对是建议的最佳蛮力方法。谢谢!
      • 这里的蛮力叫做挤压定理
      • 这就是您在区间算术中使用的那种算法。 en.wikipedia.org/wiki/Interval_arithmetic
      【解决方案8】:

      如果你想要一个离散的(而不是连续的)答案,你可以做一些类似于像素绘画算法的事情。

      在网格上绘制圆圈,然后为网格中的每个单元格着色(如果它大部分包含在一个圆圈内)(即,至少 50% 的区域在其中一个圆圈内)。对整个网格(网格跨越圆圈覆盖的所有区域)执行此操作,然后计算网格中彩色单元格的数量。

      【讨论】:

        【解决方案9】:

        这是一个在实践中应该很容易实现的算法,并且可以调整以产生任意小的错误:

        1. 用一个以同一点为中心的正多边形近似每个圆
        2. 计算作为近似圆的并集的多边形
        3. 计算合并多边形的面积

        第 2 步和第 3 步可以使用标准的、易于找到的计算几何算法来执行。

        显然,您为每个近似多边形使用的边越多,您的答案就越准确。您可以使用内接和外接多边形进行近似,以获得确切答案的界限。

        【讨论】:

          【解决方案10】:

          我一直在研究一个模拟重叠星场的问题,试图从密集场中的实际圆盘区域估计真实的恒星数量,其中较大的明亮恒星可以掩盖较暗的恒星。我也曾希望能够通过严格的形式分析来做到这一点,但无法找到该任务的算法。我通过将蓝色背景上的星域生成为绿色圆盘来解决它,其直径由概率算法确定。一个简单的例程可以将它们配对以查看是否存在重叠(将星对变为黄色);然后颜色的像素计数生成观察区域以与理论区域进行比较。然后,这会生成真实计数的概率曲线。也许是蛮力,但它似乎工作正常。

          (来源:2from.com

          【讨论】:

            【解决方案11】:

            使用所谓的功率图可以有效地解决这个问题。不过,这确实是一项繁重的数学运算,而不是我想临时解决的问题。对于“简单”的解决方案,请查找线扫描算法。这里的基本原则是将图形分成条带,计算每个条带的面积相对容易。

            因此,在包含所有没有被擦掉的圆圈的图形上,在每个位置画一条水平线,即圆的顶部、圆的底部或两个圆的交点。请注意,在这些条带内,您需要计算的所有区域看起来都一样:一个“梯形”,两侧被圆形线段取代。因此,如果您可以计算出如何计算这样的形状,您只需对所有单独的形状进行计算并将它们加在一起即可。这种朴素方法的复杂度是 O(N^3),其中 N 是图中的圆圈数。通过一些巧妙的数据结构使用,您可以将此行扫描方法改进为 O(N^2 * log(N)),但除非您真的需要,否则可能不值得这么麻烦。

            【讨论】:

              【解决方案12】:

              像素绘画方法(如@Loadmaster 所建议的)在许多方面都优于数学解决方案:

              1. 实现要简单得多。上述问题只需不到 100 行代码即可解决,as this JSFiddle solution demonstrates(主要是因为它在概念上要简单得多,并且没有需要处理的边缘情况或异常)。
              2. 它很容易适应更普遍的问题。它适用于任何形状,无论形态如何,只要它可以使用 2D 绘图库(即“全部!”)进行渲染——圆形、椭圆、样条线、多边形,应有尽有。哎呀,甚至位图图像。
              3. 像素绘画解决方案的复杂度为 ~O[n],而数学解决方案的复杂度为 ~O[n*n]。这意味着随着形状数量的增加,它的性能会更好。
              4. 说到性能,您通常会免费获得硬件加速,因为大多数现代 2D 库(我相信像 HTML5 的画布)会将渲染工作交给图形加速器。

              像素绘制的一个缺点是解决方案的准确性有限。但这可以通过根据情况需要简单地渲染到更大或更小的画布来进行调整。还要注意,2D 渲染代码中的anti-aliasing(通常默认打开)将产生比像素级更好的精度。因此,例如,将 100x100 的图形渲染到相同尺寸的画布上,我认为应该会产生大约 1 / (100 x 100 x 255) = .000039% 的精度……这可能“足够好”除了最苛刻的问题之外的所有问题。

              <p>Area computation of arbitrary figures as done thru pixel-painting, in which a complex shape is drawn into an HTML5 canvas and the area determined by comparing the number of white pixels found in the resulting bitmap.  See javascript source for details.</p>
              
              <canvas id="canvas" width="80" height="100"></canvas>
              
              <p>Area = <span id="result"></span></p>
              
              // Get HTML canvas element (and context) to draw into
              var canvas = document.getElementById('canvas');
              var ctx = canvas.getContext('2d');
              
              // Lil' circle drawing utility
              function circle(x,y,r) {
                ctx.beginPath();
                ctx.arc(x, y, r, 0, Math.PI*2);
                ctx.fill();
              }
              
              // Clear canvas (to black)
              ctx.fillStyle = 'black';
              ctx.fillRect(0, 0, canvas.width, canvas.height);
              
              // Fill shape (in white)
              ctx.fillStyle = 'white';
              circle(40, 50, 40);
              circle(40, 10, 10);
              circle(25, 15, 12);
              circle(35, 90, 10);
              
              // Get bitmap data
              var id = ctx.getImageData(0, 0, canvas.width, canvas.height);
              var pixels = id.data; // Flat array of RGBA bytes
              
              // Determine area by counting the white pixels
              for (var i = 0, area = 0; i < pixels.length; i += 4) {
                area += pixels[i]; // Red channel (same as green and blue channels)
              }
              
              // Normalize by the max white value of 255
              area /= 255;
              
              // Output result
              document.getElementById('result').innerHTML = area.toFixed(2);
              

              【讨论】:

              • 此解决方案无法说明对圆的面积进行数学计算。它错过了 OPs 问题的重点。很多时候,在处理几何形状时,渲染几何只是成功了一半
              【解决方案13】:

              Ants Aasma 的回答给出了基本的想法,但我想让它更具体一点。看看下面的五个圆圈以及它们的分解方式。

              • 蓝点是圆心。
              • 红点是圆形边界交点。
              • 红点内部为白色是圆的边界交点,不包含在任何其他圆中

              识别这 3 种类型的点很容易。现在构建一个图形数据结构,其中节点是蓝点和内部为白色的红点。对于每个圆,在圆中间(蓝点)和它的每个交点(红点,内部为白色)之间放置一条边。

              这将圆形联合分解为一组多边形(蓝色阴影)和圆形饼块(绿色阴影),它们成对不相交并覆盖原始联合(即分区)。由于这里的每一块都是很容易计算面积的东西,因此您可以通过对块的面积求和来计算并集的面积。

              【讨论】:

              • 我想我可以相当容易地计算出一组红点/白点,但是我的图论不是太好:从算法上讲,你如何从节点 + 边列表中获得计算区域?
              • 算法可以通过使用一组不重叠的三角形而不是多边形来简化。弧线(绿色区域)是仅包含在一个圆圈中的区域。随着您添加更多的圆圈,扩大多边形的大小。 (最后你会忘记你甚至在谈论多边形)。它使布尔属性和面积也更容易计算。当一个空心的红点变成一个实心的红点时,你只需在你的集合中添加更多的三角形,然后调整它被越来越多的相交圆圈“吃掉”的弧线。
              • 如何将多边形和圆弧与一组蓝色和红色/白色点区分开来?
              【解决方案14】:

              这可以使用 格林定理 来解决,复杂度为 n^2log(n)。 如果您不熟悉 格林定理 并想了解更多,这里是可汗学院的 videonotes。但是为了我们的问题,我想我的描述就足够了。

              如果我把 LM 这样放

              那么 RHS 就是区域 R 的面积,可以通过求解闭积分或 LHS 获得,这正是我们要做的。

              所以沿逆时针方向积分给我们区域的面积,而沿顺时针方向积分给我们面积的负值。所以

              AreaOfUnion =(沿逆时针方向沿红色弧线积分 + 沿顺时针方向沿蓝色弧线积分)

              但是很酷的技巧是,如果对于每个圆,如果我们对不在任何其他圆内的弧进行积分,我们就会得到所需的区域,即我们沿着所有红色圆弧在逆时针方向上积分,沿着顺时针方向沿着所有蓝色圆弧积分方向。 工作完成!!!

              即使是圆不与其他圆相交的情况也会被采用 照顾。

              这是我的C++ Code的GitHub链接

              【讨论】:

                【解决方案15】:

                我有办法得到一个大概的答案如果你知道你所有的圆圈都将在一个特定的区域内,即圆圈中的每个点都在你知道尺寸的盒子内.例如,如果所有圆圈都在已知大小的图像中,则该假设将是有效的。如果您可以做出此假设,请将包含您的图像的区域划分为“像素”。对于每个像素,计算它是否在至少一个圆圈内。如果是,则将运行总计加一。完成后,您知道至少一个圆圈内有多少像素,并且您还知道每个像素的面积,因此您可以计算所有重叠圆圈的总面积。

                通过增加您所在区域的“分辨率”(像素数),您可以改进近似值。

                此外,如果包含圆圈的区域的大小是有界的,并且您保持分辨率(像素数)不变,则算法在 O(n) 时间内运行(n 是圆圈数)。这是因为对于每个像素,您必须检查它是否在您的 n 个圆圈内,并且像素的总数是有界的。

                【讨论】:

                  猜你喜欢
                  • 2014-05-31
                  • 2013-12-09
                  • 2021-10-10
                  • 2018-06-01
                  • 1970-01-01
                  • 2020-02-10
                  • 1970-01-01
                  • 2021-03-12
                  • 2012-02-02
                  相关资源
                  最近更新 更多