【问题标题】:Finding the smallest circle that encompasses other circles?找到包含其他圆圈的最小圆圈?
【发布时间】:2010-01-18 08:30:12
【问题描述】:

如果一个圆是由它的中心的 X、Y 和一个半径定义的,那么我怎样才能找到一个包含给定数量的圆的圆?一个完整的圆圈,它是完全包含 2 个或更多任意大小和位置的圆圈的最小圆圈。

起初我尝试通过找到中心的中点来包含 2 个圆,并且该中点是新圆的中点,而半径等于 2 个初始圆的半径的一半和它们中心之间距离的一半,但不知何故,结果总是有点不对劲。问题似乎总是找半径的问题,但我为此头疼我无法让它工作。

我不一定需要找到包含 3 个或更多圆圈的圆圈的方法。我可以找到一个包含 2 的圆圈,将那个圆圈与另一个圆圈和另一个圆圈包围起来,最后的圆圈应该包含整个步骤中给出的所有圆圈。

【问题讨论】:

  • 不要认为这是 StackOverflow 的问题。问你的数学老师。这不是家庭作业吗?
  • @FractalizeR 圆形和球体通常用于图形和游戏中,以逼近移动对象的边界框以进行碰撞检测和其他用途。
  • @David Hall:mathoverflow 适用于数学专业人士。他们不想要这样的问题。
  • 我认为这个问题并不令人讨厌,它可能与编程有关。提供的近似值(忽略所有其他圆圈,取其中两个......)从数学角度来看是合理的(至少对于两个圆圈问题),因此结果“有点偏离”可能表明存在编程问题(您使用的是什么类型?您是否考虑舍入错误?)
  • 我对数学问题的启发是:如果数学将被翻译或在代码中执行,它属于。如果数学将用于避免代码,它可能不会。这个数学变成代码,所以这些问题对 SO 有好处。

标签: c# math geometry


【解决方案1】:

给定两个圆,圆心为 [x1,y1],[x2,y2],半径分别为 R1 和 R2。封闭圆的中心是什么?

假设 R1 不大于 R2。如果第二个圆圈较小,则只需交换它们即可。

  1. 计算圆心之间的距离。

    D = sqrt((x1-x2)^2 + (y1-y2)^2)

  2. 第一个圆圈是否完全位于第二个圆圈内?因此,如果 (D + R1)

  3. 如果 (D+R1) > R2,则包围圆的半径为 (D+R1+R2)/2

在后一种情况下,封闭圆的中心必须位于连接两个中心的直线上。所以我们可以把新中心写成

center = (1-theta)*[x1,y1] + theta*[x2,y2]

theta 由下式给出

theta = 1/2 + (R2 - R1)/(2*D)

请注意,theta 始终为正数,因为我们已确定 (D+R1) > R2。同样,我们应该能够确保 theta 永远不会大于 1。这两个条件确保封闭中心严格位于两个原始圆心之间。

【讨论】:

  • 虽然两个圆的情况很简单,但它并不能有效地推断出 N 个圆的中心。
  • 我同意这个算法不需要扩展到 n 圈。但是,直接的问题是包含两个圆圈的最小圆圈。
  • 同意,是 OP 想像一个 2-circle 解决方案很容易有效地扩展到 N :)
【解决方案2】:

您手头的问题称为球体的最小封闭球体。我已经写了关于它的论文,请参阅"Smallest enclosing ball of balls",苏黎世联邦理工学院。

您可以在包Bounding Volumes 中的Computational Geometry Algorithms Library (CGAL) 中找到一个非常高效的C++ 实现。 (无需使用所有 CGAL;只需提取所需的源文件和头文件即可启动并运行。)

注意:如果您正在寻找一种算法来计算仅由点组成的最小封闭球体,还有其他实现,请参阅this post。 p>

【讨论】:

  • +1 这篇文章显然应该得到更多的支持,因为你的论文完全解决了问题!
【解决方案3】:

因为我的不精确解决方案不受欢迎。这是获得确切解决方案的方法。但是它很慢( O(N^4)? )并且计算上很讨厌。 (不像不精确的方法)

首先,您需要知道给定三个圆,我们可以找到一个与它们相切的圆,而不是包含所有三个圆。这是阿波罗尼奥斯的圈子之一。你可以从mathworld获取算法。

接下来,您可以证明 N 个圆中最小的封闭圆与 N 个圆中的至少 3 个相切。

要找到这个圈子,我们执行以下操作

  1. 循环遍历所有三元组 - O(N^3)
  2. 找到这 3 个圆的封闭 Apollonius 圆 - 计算上很讨厌
  3. 如果它包含所有圆圈,则将其添加到潜力列表中 - 检查为 O(N)
  4. 解决方案是具有最小半径的潜力

可能有一些技巧可以加快此过程,但它应该为您提供确切的解决方案。 将Smallest Enclosing Circle 算法变为线性时间的一些“技巧”可能适用于这里,但我怀疑它们不会是微不足道的适应。

【讨论】:

  • 哎呀在这里出错了,包围三个圆的最小圆是一个圆(如果其他圆包含在其中),一个与其中两个相切的圆(如果另一个是包含在这个圆内)或与所有三个相切的圆(阿波罗尼奥斯圆)。请注意,这不会影响运行时间,但会稍微改变第 2 步。
【解决方案4】:

我现在建议不要这样做
请参阅下面的讨论。

最初的想法

我会考虑迭代推拉方法。

  1. 猜猜中心的位置(最简单的是所有中心的平均位置)
  2. 计算到每个圆上最远点的向量。这些总是朝着那个圆的中心方向,长度为distance_to_center_of_circle[i]+radius_of_circle[i],并在你走的时候形成向量和。另请注意,当前位置所需的半径是这些长度中的最大值。
  3. 提出从 2 开始的向量和的 1/5 或 1/10 的步骤,并从 2 开始为新点重做计算
  4. 如果新点需要比旧点更小的圆,则将新点设为当前点,否则,拆分差异,减小建议步长的大小(例如减半)。
  5. 转到 3

当它停止 [+] 收敛时你就完成了。

Nikie 戳它直到...

根据要求澄清第二步。将要测试的位置称为\vec{P}(向量)。[++] 将每个圆的中心称为\vec{p}_i(也是向量),每个圆的半径为r_i。形成和\sum_i=1^n \hat{p_i - P}*|(p_i-P)|+r_i)。[+++] 和的每个元素都指向从当前评估点到相关圆心的方向,但比r_i 长。和本身就是一个向量。

半径R需要包围从P开始的所有圆就是max(|p_i-P|_r_i)

病理病例

我不认为 nikie 提出的特殊情况有问题,但它让我陷入了这种算法失败的情况。失败是未能改进解决方案之一,而不是分歧之一,但仍然......

考虑四个半径为 1 的圆

(-4, 1)
(-5, 0)
(-4, 1)
( 5, 0)

起始位置为(-1, 0)。设计对称,因此所有距离都位于 x 轴上。

正确的解决方案是(0, 0),半径为 6,但在步骤 2 中计算的向量大约是 ::calculates furiously:: (-.63, 0),指向错误的方向导致永远找不到向原点的改进。

现在,上面的算法将实际选择(-2, 0) 作为起点,这给出了 ::calculates furiously:: 的初始向量总和大约 +1.1。因此,在 (3) 上选择错误的步长会导致不太理想的解决方案。 ::叹气::

可能的解决方案:

  • 在 (3) 中,抛出一个介于(例如 +1/5 和 -1/5)之间的随机分数,可能向正数加权。
  • 在 (4) 中,如果步骤被拒绝,只需返回步骤三而不更改步长限制。

然而,在这一点上,它并不比纯粹的随机游走好多少,而且你没有一个简单的条件来知道它何时收敛。嗯。

[+] 当然,或者放慢到您满意的程度。 [++] 使用乳胶表示法。 [+++] 这里\hat{}表示与参数指向同一方向的归一化向量。

【讨论】:

  • +1 表示具体而生动的建议。我认为在某些情况下它可能不会收敛到最佳解决方案,因为 2 个圆距当前中心的距离相等,将中心移动到任一圆都会增加半径。 (想象为形成一个钝角等角三角形的 3 个中心点找到圆)
  • @nikie 无论哪种方式,我都很难说服自己。如果它可以卡住,您可以采用蒙特卡洛方法将其松开。这是否值得取决于您真正需要最低限度的程度。
  • @dmckee:为了说服自己,画两点 A 和 B,相隔 3 个单位。然后围绕每个点画一个半径为 2 个单位的圆。调用其中一个圆-圆交点 C。假设 A 和 B 是给定的输入点,C 是当前估计的中心。显然 C 不是最优的。但是C和A之间的所有点都在B周围的圆之外,所以向A移动会使中心远离B,所以它会增加半径。 (对于 B 反之亦然)。因此,算法将终止。
  • @nikie 我不认为这种情况有问题。步骤 2 中的向量和现在指向 AB 中心的 D 方向,所以我相信它会继续朝着正确的方向工作。如果有两个不等效的局部最小值,它可能会卡住。 (我改编的算法相当稳健(尽管我们在确实承认病态病例的噪声数据上使用它)。
  • @dmckee:我想我误解了你的算法。你能澄清步骤(2)吗?如果你只是简单地将当前中心位置到输入中心点的所有向量相加,这将如何收敛? (例如,假设所有的半径都是 0。你从平均位置开始。如果你从平均位置到每个输入点的向量和,你总是会得到 0。)
【解决方案5】:

我听取了你们中的一些人的意见,这是我发现的解决方案:

public static Circle MinimalEnclosingCircle(Circle A, Circle B) {
            double angle = Math.Atan2(B.Y - A.Y, B.X - A.X);
            Point a = new Point((int)(B.X + Math.Cos(angle) * B.Radius), (int)(B.Y + Math.Sin(angle) * B.Radius));
            angle += Math.PI;
            Point b = new Point((int)(A.X + Math.Cos(angle) * A.Radius), (int)(A.Y + Math.Sin(angle) * A.Radius));
            int rad = (int)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2)) / 2;
            if (rad < A.Radius) {
                return A;
            } else if (rad < B.Radius) {
                return B;
            } else {
                return new Circle((int)((a.X + b.X) / 2), (int)((a.Y + b.Y) / 2), rad);
            }
        }

圆由圆心的X、Y和半径定义,都是整数。有一个构造函数是 Circle(int X, int Y, int Radius)。在打破了一些旧的三角概念之后,我认为最好的方法是找到圆上相距最远的 2 个点。一旦我有了它,中点将是中心,一半的距离将是半径,因此我有足够的空间来定义一个新的圆。如果我想包含 3 个或更多圆圈,我首先在 2 个圆圈上运行,然后在生成的包围圆圈和另一个圆圈上运行,依此类推,直到最后一个圆圈被包围。可能有一种更有效的方法可以做到这一点,但现在它有效,我对此很满意。

我觉得回答自己的问题很奇怪,但如果没有每个人的想法和链接,我不可能得出这个解决方案。谢谢大家。

【讨论】:

  • 实际上我对此进行了测试:Circle(25,30,20) 和 Circle(45,30,40),它只返回第二个 Circle(这是不正确的)。
  • 我不知道你是怎么得出这个结论的,我创建了这 2 个圆圈,返回给我的圆圈是 Circle(45, 29, 40)。它是整数数学,它解释了为什么它略有偏差,但这是正确的。请查看我在coreyogburn.com/page/Circle-Library.aspx 的项目以查看我正在测试的实现。如果您仍然发现问题,请在此处或此处告诉我。
  • 没关系。问题是我使用的绘图程序。它使圆的半径减半。 Circle(45, 30, 40) 确实是一个正确的 Circle。很抱歉造成混乱。
  • 感谢您的仔细检查。
  • 嘿科里 - 我正在尝试找出如何解决这个确切的问题。我还没有测试过这个算法,但我认为你可以先在两个上测试它,然后在下一个圆圈中使用这个结果,依此类推。例如 - 如果您绘制一个等边三角形,并放置 3 个小圆圈,每个小圆圈的中心位于每个顶点,则包含前两个圆圈的最小圆圈的几乎一半将溢出包含三个圆圈的最小圆圈。
【解决方案6】:

所以如果你不需要精确的圆,这个近似值可能会做。

  1. 取所有圆心的平均值,称之为 X点
  2. 令 R1 为从 X 到圆心的最大距离。
  3. 令 R2 为圆的最大半径

所有的圆都必须落在以 X 为中心、半径为 R1+R2 的圆内

【讨论】:

  • 确实不会,但它可能会提供比 OP 最后一段中概述的迭代方法更好的结果。因此,我假设一个简单但不精确的方法是可以接受的。
【解决方案7】:

这不是一个小问题。我还没有阅读上面所有的答案,所以如果我重复某人已经说过的话,那就是我的错。

每个圆 c_i 由 3 个参数 x_i,y_i,r_i 定义

最优圆C*需要找到3个参数x*,y*,r*

C* 包含所有 i 的 c_i

令 d_i = ||(x,y)-(x_i,y_i)|| + r_i

那么如果 r 是包含所有 c_i 的圆的半径,则 r >= d_i for all i

我们希望 r 尽可能小

所以,r* = max(d_i)

因此我们想要最小化 d_i 的最大值

所以 (x*,y*) 由 max(d_i) 的 arg min 给出。一旦找到 (x*,y*),r* 就可以很容易地计算出来,并且等于 max(d_i)。这是一个极小极大问题。

为了让事情更容易理解,只考虑 2 个圆圈,我们如何找到 (x*,y*)?

(x*,y*) 可以通过找到最小化 (d_1 - d_2)^2 的 (x,y) 来找到。一般情况下

让 e_ij = (d_i - d_j)^2

然后定义 e = \sum e_ij for i != j (这个和中有 n 选择 2 项)

(x*,y*) = arg min of e

这就是需要解决的问题。

提示:如果所有 i 的 r_i = 0,那么当输入是一堆点时,这将简化为传统的最小封闭圆问题,并且我们希望找到覆盖所有这些点的最小圆。

【讨论】:

    【解决方案8】:

    只需了解circle 的方程式并推导出一个方程式(或一系列)以找到答案,然后开始实施。鉴于您已经做了一些事情,也许我们可以帮助您。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-07
      • 2012-08-01
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-16
      相关资源
      最近更新 更多