Answers:
这不是一个通用的解决方案,因为在多种情况下它无法提供距白点最短距离的蓝色圆圈的位置。例如,如果您将100个红球组合在一起,并且白点距离这组红球较远,那么任何一个红球都不会对仅位于白点中心的蓝色圆圈的位置产生任何影响。它也不会显示所有计算详细信息。无论如何,对于配置的子集,其中解决方案(蓝色圆圈)与两个红色圆圈相切,则下面的方法应该起作用:
1)令R为蓝色圆圈的半径
2)在所有红色圆圈对上循环,是的我知道这是O(n2)。
3)对于中心分别在(xi,yi)和(xj,yj)且半径分别为ri和rj的每对圆i,j,计算这对圆之间的距离的平方
d_ij^2=(xi-xj)^2+(yi-yj)^2
4)将所有成对的圆圈
dij^2<R^2
进入列表。
5)遍历列表,找到与圆i和圆j相切的半径为R的圆的2个解。为此,请结合使用这些方程式和此图像
a = R+ri
b = R+rj
c = dij
α = arccos((b^2+c^2-a^2)/(2bc)
通过以上信息,您可以找到(X1ij,Y1ij)和(X2ij,Y2ij)与圆i和j相切的两个圆的中心。对于每个候选蓝色圆圈,在所有其他红色圆圈上循环,看看它是否不重叠。如果他们不满意,请检查到白色圆圈的距离。如果您保持较小的距离,那么当您完成对圆对列表的遍历时,我认为您将会找到解决方案。该算法看起来像O(n3)。
距该点最近的位置将在该点上或触摸一个圆。
因此,首先检查该点,然后在每个现有圆的边缘周围滚动新的圆,计算到该点的距离,以及如果您在进行过程中重叠并跟踪最小距离点。遍历每个圆圈后停止。
即。检查绿线上的所有点以及白色圆圈。绿线是半径为红色加上蓝色的圆
您需要检查整个绿线,而不仅仅是交叉点,以便覆盖这些边缘情况。
显然,遍历的步长对于性能而言非常重要。但是,由于您认为性能不是问题,因此请选择与您的输出值的分辨率相对应的值。即浮动,长?
澄清:
我的建议是对每个圆上的所有点进行蛮力测试,以使其在每个点上与所有其他圆重叠。没有聪明。
如果示例图片指示圆圈数和分辨率,则对于标准PC来说应该不成问题
我们有20个平均半径为200的圆,因此大约有20 * 2π* 200点* 20个相交测试= 4800000次迭代
注意:
这样的迭代方法存在缺陷,因为步长(在这种情况下为输出分辨率)会极大地影响结果。
假设我有两个相距2个像素的红色圆圈和一个1像素半径的蓝色圆圈要在它们之间挤压。显然,以两个像素中的任何一个作为蓝色圆圈的中心,它将与红色之一重叠。但是,如果中心位于两个像素之间,则显然有圆圈的空间。
因此,我的评论询问输出的分辨率。你说的可以是任何东西。
您也可以求解半径为蓝色圆形半径的每对圆形的联立方程。
这将为您提供一个点,在该点上,蓝色圆圈将比迭代更准确地接触两个红色圆圈。
然而。在某些情况下,如果仅执行此操作,则会得到错误的答案或没有答案。即。
1个或没有圈子
2个或更多的圆,但目标点距离较远且在其外部。
许多圆,但目标点靠近表面
这个代码包含工作代码,
概念
给定的圆圈是C1,C2 .... Cn
圆Cn的坐标为Cnx,Cny且半径为Cr
所需圆的半径为R
如果蓝色圆圈在X,Y位置并且与其他任何圆圈不冲突,则以下等式为真
(C1x - X)^2 + (C1y - Y)^2 > (C1r + R)^2
(C2x - X)^2 + (C2y - Y)^2 > (C2r + R)^2
....
(Cnx - X)^2 + (Cny - Y)^2 > (Cnr + R)^2
改变第一个方程,
C1x^2 - 2C1x*X + X^2 + C1y^2 - 2C1y*Y + Y^2 > C1r^2 + 2C1r*R + R^2
X^2 + Y^2 - 2C1x*X - 2C1y*Y > C1r^2 + 2C1r*R + R^2 - C1x^2 - C1y^2
因此方程式可以重写为
X^2 + Y^2 - 2C1x*X - 2C1y*Y > C1r^2 + 2C1r*R + R^2 - C1x^2 - C1y^2
X^2 + Y^2 - 2C2x*X - 2C2y*Y > C2r^2 + 2C2r*R + R^2 - C2x^2 - C2y^2
....
X^2 + Y^2 - 2Cnx*X - 2Cny*Y > Cnr^2 + 2Cnr*R + R^2 - Cnx^2 - Cny^2
实作
从白点(Xw,Yw)的坐标开始,
var isValidLocation = function(x,y,r){
var valid = true;
for (var i = 0; i< circles.length; i++){
var circle = circles[i];
valid = valid && ((x*x + y*y - 2*circle.x*x - 2*circle.y*y) > (circle.radius*circle.radius + 2*circle.radius*r + r*r - circle.x*circle.x - circle.y*circle.y));
}
return valid;
};
var find = function(Xw,Yw,Rw){
var radius = 0;
while(true){
for (var x=-1 * radius ;x <= radius; x++) {
for (var y=-1 * radius;y <= radius; y++) {
if (isValidLocation(Xw + x,Yw + y, Rw)){
drawCircle(Xw + x,Yw + y,Rw,"#0000FF");
return;
}
}
}
radius++;
}
};
发现满足所有方程的第一个坐标是蓝色圆圈的位置
扩展的圆是半径为r的圆之一
如果O不在圆心处,则需要做一些额外的工作。所以现在假设O == C0
通过使用它们各自的半径加上r来计算所有圆与C0的所有交点,即,使延伸圆与扩展的C0相交。如果没有相交,则您要寻找的点在C0上的任意位置,如果有相交,则对于每个相交,检查它是否在另一个扩展圆内(您可以将自己限制为与C0相交的圆)。以不在另一个扩展圆中的第一个交点作为P,可能会有其他交点。
如果扩展圆和C0之间没有在另一个扩展圆之内的交点,请计算所有扩展圆的交点。然后按照与O的距离顺序检查这些交点,再将这些交点放在另一个扩展的圆内,如果是,则舍弃,如果不是,则得出结果。
如果您设想此设想,请在所有圆周围画一条线以指示您的蓝色圆圈的可能位置,将所有扩展圆的并集将指示您的蓝色圆圈无法覆盖的区域。您要查找的点是不在该联合中的最接近点。如果C0上的某个点不在该联合中作为解决方案,则如果C0被完全覆盖,则P必须位于其他两个扩展圆之间的交点上,并且必须位于未被C覆盖的区域中此联合(即不在扩展的圈子中)。
这是O(n ^ 2),尽管有一些方法可以改善这一点,但是可以使用网格来减少成对搜索的工作量,我也认为圆是根据它们与O的接近程度来排序的(收音机减少两个圆圈)将有助于限制覆盖范围和交叉点搜索的搜索空间
现在,您有了一组可能的解决方案,对它们进行迭代并检查每一个。
注意:我并不是说算法的实现应该被准确描述。您可以尝试使用动态编程来提高性能,或者在显然无法使用的情况下跳过可能的解决方案。