寻找最小的覆盖其他圆的圆?

14

如果一个圆由它的中心点的X,Y坐标和半径定义,那么如何找到一个包含给定数量圆的圆?一个最小的圆,足以完全容纳任何大小和位置的2个或多个圆。

起初,我试图通过找到中心点的中点,并使其成为新圆的中点,而半径等于两个初始圆的半径的一半和它们中心之间距离的一半来包含2个圆,但是总是稍有偏差。问题似乎总是出现在找半径的问题上,但是我对此头痛不已,无法使其正常工作。

我不一定需要找到一个包含3个或更多圆的圆的方法。 我可以找到一个包含2个圆的圆,然后用另一个圆将其环绕,再用另一个圆环绕它,最后的圆应该包含所有在步骤中给出的圆。


1
不要认为这是一个适合在StackOverflow上提问的问题。去问你的数学老师吧。这不是作业吗? - Vladislav Rastrusny
3
在图形和游戏中,圆和球经常用来近似移动物体的边界框,用于碰撞检测和其他目的。 - Eugene Yokota
14
@David Hall: mathoverflow是为数学专业人士而设的。他们不希望有像这样的问题。 - balpha
2
我并不认为这个问题很麻烦,而且它很可能与编程有关。从数学角度来看,所提供的近似值(忽略所有其他圆,只取其中两个...)是正确的(至少对于两个圆的问题),因此结果“有点偏差”可能表明存在编程问题(你使用了什么类型?是否考虑到了舍入误差?)。 - David Rodríguez - dribeas
2
我的启发式算法是:如果数学问题将被翻译成或在代码中执行,它就适合放在程序中。如果数学问题将用于避免编写代码,那么它可能不适合。这个数学问题变成代码,所以这个问题适合发布在SO上。 - dmckee --- ex-moderator kitten
显示剩余12条评论
8个回答

13

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

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

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

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

  2. 第一个圆是否完全位于第二个圆内?如果 (D + R1) ≤ R2,则完成了。将较大的圆作为包围圆返回,半径为 R2,中心为 [x2,y2]。

  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。这两个条件确保了包围中心严格位于两个原始圆心之间。

6
虽然两个圆的情况很简单,但无法推广到高效地找到N个圆的中心。 - Will
我同意这个算法不需要扩展到n个圆。然而,直接的问题是关于一个最小的圆来包含两个圆。 - user85109
同意,是楼主想象中的一个可以轻松高效地扩展到N个圆的2圆解决方案 :) - Will

8
你手头的问题被称为“球的最小外接球”。我写过我的论文,可以参考"Smallest enclosing ball of balls",来自苏黎世联邦理工学院。你可以在计算几何算法库(CGAL)的包Bounding Volumes中找到一个非常高效的C++实现。(不需要使用CGAL的所有功能;只需提取所需的源和头文件即可开始运行。) 注意:如果你正在寻找一个计算仅由点组成的最小外接球的算法,那么可以看看其他实现,具体请参考此帖子

1
+1 这篇帖子显然应该有更多的赞,因为你的论文正好涉及到这个问题! - Jendas

4
由于我的不精确解决方案不被喜欢。这里有一种获得精确解决方案的方法。但它很慢(O(N ^ 4)?)并且计算上很麻烦。(与不精确的方法不同)
首先,您需要知道,给定三个圆,我们可以找到一个包含所有三个圆的切圆。这是阿波罗尼斯圆之一。您可以从mathworld获取算法。
接下来,您可以证明最小包围圆对于N个圆至少与其中3个圆相切。
为了找到这个圆,我们执行以下操作:
1.循环遍历所有三元组 - O(N ^ 3) 2.找到这3个圆的包围阿波罗尼斯圆-计算上很麻烦 3.如果它包含了所有的圆,则将其添加到潜在列表中-检查为O(N) 4.具有最小半径的潜在解决方案

可能有一些技巧可以加速这个过程,但它应该能给你一个准确的解决方案。 使最小外接圆算法达到线性时间的一些“技巧”可能适用于这里,但我怀疑它们不是微不足道的改编。


翻译:糟糕,在这里犯了一个错误。包含三个圆的最小圆要么是其中一个圆(如果其他两个圆都被它包含),要么是与其中两个圆相切的圆(如果第三个圆被包含在此圆内),或者是与所有三个圆相切的圆(阿波罗尼斯圆)。请注意,这不会影响运行时间,但会稍微改变第二步骤。 - Michael Anderson

3

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

原始想法

我会考虑使用迭代推拉方法。

  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长。总和本身是一个矢量量。

需要包含来自P的所有圆的半径Rmax(|p_i-P|_r_i)

病态案例

我不认为Nikie提出的特定案例是问题,但它让我想到了一种算法失败的情况。失败不是发散的问题,而是不能改进解决方案的问题,但仍然......

考虑四个半径均为1的圆,位于

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

起始位置为(-1, 0),对称设计使得所有距离都沿x轴。

正确的解法是半径为6的(0, 0),但步骤2计算出的向量大约为::疯狂计算:: (-.63, 0),指向错误方向导致无法找到朝向原点的改进。

现在,上述算法实际上会选择(-2, 0)作为起始点,这会给出一个初始向量和为::疯狂计算:: 约+1.1。因此,在(3)上选择不良的步长大小会导致不太理想的解决方案。::叹气::

可能的解决方案:

  • 在(3)中抛出一个随机分数(例如+1/5和-1/5之间),可能偏向于正面。
  • 如果步骤被拒绝,则简单地返回步骤三,而不改变步长限制。

然而,此时它与纯随机漫步相比并没有更好,并且您没有易于知道何时收敛的条件。嗯。

[+] 或者按您的要求减速。 [++] 使用latex符号。 [+++] 这里\hat{}表示指向与参数相同方向的归一化向量。


我认为有些情况下可能无法收敛到最优解,因为两个圆距离当前中心点相等,将中心点移动到任何一个圆都会增加半径。(想象一下找到三个中心点形成的钝角等腰三角形的圆)+1建议: - Niki
@dmckee:为了让自己相信,画出两个距离为3个单位的点A和B。然后在每个点周围画一个半径为2个单位的圆。将其中一个圆与另一个圆相交的点称为C。想象一下,A和B是给定的输入点,C是当前估计的中心点。显然,C不是最优解。但是,在C和A之间的所有点都在B周围的圆外面,所以向A移动会使中心点离B更远,从而增加半径(对于B来说也是如此)。因此,算法会终止。 - Niki
@dmckee:首先,针对您的澄清:取\sum_i=1^n \hat{p_i - P}*|(p_i-P)|+r_i)。如果r_i=0,则表达式简化为\sum_i=1^n (p_i - P)。当该表达式为0时,收敛就已经实现了。如果P是p_i的平均值,那么就是这种情况。 - Niki
但是你可以很容易地解决这个问题。粗略地说:从任意位置开始,沿着具有最大距离的切点方向移动(这就是我最初误读你的算法的方式)。当存在多个具有最大距离的点时,需要进行一些特殊处理。 - Niki
@nikie:不错。特殊处理可能是使用有问题方向的矢量和。 - dmckee --- ex-moderator kitten
显示剩余3条评论

2

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

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)。在研究一些旧的三角概念后,我发现找到距离最远的两个圆上的点是最好的方法。一旦我有了那个,中点就是圆心,一半的距离就是半径,因此我有足够的信息来定义新的圆。如果我想包围3个或更多个圆,我首先在2个圆上运行此操作,然后在结果圆和另一个圆上运行此操作,直到最后一个圆被包围为止。可能有更有效的方法来做到这一点,但现在它可以工作,我很满意。
我感觉回答自己的问题很奇怪,但没有大家的想法和链接,我不可能得出这个解决方案。谢谢大家。

实际上,我测试了Circle(25,30,20)和Circle(45,30,40),但它只返回第二个Circle(这是不正确的)。 - Chris Redford
1
我不确定你是怎么得出那个结论的,因为我创建了这两个圆,而返回给我的圆是Circle(45, 29, 40)。这是整数运算导致略有偏差,但这是正确的。请查看我的项目网址http://coreyogburn.com/page/Circle-Library.aspx,以查看我正在测试的实现。如果您仍然发现问题,请在那里或在此处告诉我。 - Corey Ogburn
没关系。问题出在我使用的绘图程序上。它将圆的半径减半了。Circle(45, 30, 40) 确实是一个正确的圆。对于造成的困惑,我很抱歉。 - Chris Redford
我很感激你的再次确认。 - Corey Ogburn
嗨,Corey - 我在尝试解决这个确切的问题。我还没有测试这个算法,但我认为你可以在前两个上测试它,然后将该结果与下一个圆一起使用的想法存在问题。例如 - 如果你画一个等边三角形,并在每个顶点处放置3个小圆,每个小圆都有其自己的中心,几乎有一半最小圆会溢出包含前两个的最小圆,而这个最小圆则包含了三个圆。 - Steph Thirion
对于未来的读者--请查看页面:http://en.wikipedia.org/wiki/Problem_of_Apollonius,该页面提供了一个关于切线于3个圆的代数解法。只需使用-1作为符号即可找到最远的圆。仅当两个圆处于完全相反的方向时,才有2个圆的解决方案。 - Eric

0

如果您不需要精确的圆形,则此近似可能可行。

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

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


确实不会,但它很可能比OP最后一段概述的迭代方法给出更好的结果。因此我假设一个简单但不精确的方法是可以接受的。 - Michael Anderson

0

这不是一个琐碎问题。我没有读过上面所有的答案,所以如果我重复了别人已经说过的话,那是我的错。

每个圆 c_i 由 3 个参数 x_i、y_i 和 r_i 定义。

需要找到 3 个参数 x*、y* 和 r* 来定义最优圆 C*。

C* 要包含所有的 c_i。

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

然后,如果一个圆的半径 r 能够包含所有的 c_i,那么 r >= d_i 对于所有的 i 成立。

我们希望 r 尽可能小。

所以,r* = max(d_i)。

因此,我们要使 d_i 的最大值最小化。

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

为了更容易理解,考虑只有 2 个圆,如何找到 (x*,y*)?

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

令 e_ij = (d_i - d_j)^2

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

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

这就是需要解决的问题。

提示:如果对于所有 i,r_i = 0,则当输入为一堆点并且我们想要找到覆盖它们所有的最小圆时,这将简化为传统的最小包围圆问题。


-1

只需理解的方程式并推导出一个方程式(或一系列方程式)以找到答案,然后开始实现。如果您已经做了一些工作,也许我们能够帮助您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接