Algorithm 圆分离距离-最近邻问题
在二维平面上有一组给定位置和半径的圆。我想确定每个圆是否与其他圆相交,以及分隔这两个圆所需的距离。在我当前的实现中,我只需要遍历所有可能的圆组合,然后进行计算。不幸的是,这个算法是O(n^2),速度很慢 这些圆通常是成组的,它们的半径相似(但不同)。圆圈数的近似最大值约为200。算法不一定要精确,但应该很接近 下面是我目前在JavaScript中的一个(缓慢)实现:Algorithm 圆分离距离-最近邻问题,algorithm,nearest-neighbor,Algorithm,Nearest Neighbor,在二维平面上有一组给定位置和半径的圆。我想确定每个圆是否与其他圆相交,以及分隔这两个圆所需的距离。在我当前的实现中,我只需要遍历所有可能的圆组合,然后进行计算。不幸的是,这个算法是O(n^2),速度很慢 这些圆通常是成组的,它们的半径相似(但不同)。圆圈数的近似最大值约为200。算法不一定要精确,但应该很接近 下面是我目前在JavaScript中的一个(缓慢)实现: // Makes a new circle var circle = function(x,y,radius) { ret
// Makes a new circle
var circle = function(x,y,radius) {
return {
x:x,
y:y,
radius:radius
};
};
// These points are not representative of the true data set. I just made them up.
var points = [
circle(3,3,2),
circle(7,5,4),
circle(16,6,4),
circle(17,12,3),
circle(26,20,1)
];
var k = 0,
len = points.length;
for (var i = 0; i < len; i++) {
for (var j = k; j < len; j++) {
if (i !== j) {
var c1 = points[i],
c2 = points[j],
radiiSum = c1.radius+c2.radius,
deltaX = Math.abs(c1.x-c2.x);
if (deltaX < radiiSum) {
var deltaY = Math.abs(c1.y-c2.y);
if (deltaY < radiiSum) {
var distance = Math.sqrt(deltaX*deltaX+deltaY*deltaY);
if (distance < radiiSum) {
var separation = radiiSum - distance;
console.log(c1,c2,separation);
}
}
}
}
}
k++;
}
//创建一个新的循环
变量圆=函数(x,y,半径){
返回{
x:x,
y:y,
半径:半径
};
};
//这些点不代表真实数据集。我只是编出来的。
变量点=[
圆圈(3,3,2),
圆圈(7,5,4),
圆圈(16,6,4),
圆圈(17,12,3),
圆圈(26,20,1)
];
var k=0,
len=点。长度;
对于(变量i=0;i
另外,如果您能用通俗易懂的英语解释一个好的算法(KD-Tree?),我将不胜感激:-/对于初学者,如果您跳过SQRT调用,您上面的算法将大大加快。这是最著名的比较距离的简单优化。您还可以预先计算“平方半径”距离,这样就不会重复地重新计算它 此外,在您的一些算法中似乎还有很多其他的小错误。下面是我对如何修复它的看法 另外,如果您想摆脱O(N平方)算法,可以考虑使用一个。构建KD树需要前期成本,但搜索最近邻居的速度也会更快
function Distance_Squared(c1, c2) {
var deltaX = (c1.x - c2.x);
var deltaY = (c1.y - c2.y);
return (deltaX * deltaX + deltaY * deltaY);
}
// returns false if it's possible that the circles intersect. Returns true if the bounding box test proves there is no chance for intersection
function TrivialRejectIntersection(c1, c2) {
return ((c1.left >= c2.right) || (c2.right <= c1.left) || (c1.top >= c2.bottom) || (c2.bottom <= c1.top));
}
var circle = function(x,y,radius) {
return {
x:x,
y:y,
radius:radius,
// some helper properties
radius_squared : (radius*radius), // precompute the "squared distance"
left : (x-radius),
right: (x+radius),
top : (y - radius),
bottom : (y+radius)
};
};
// These points are not representative of the true data set. I just made them up.
var points = [
circle(3,3,2),
circle(7,5,4),
circle(16,6,4),
circle(17,12,3),
circle(26,20,1)
];
var k = 0;
var len = points.length;
var c1, c2;
var distance_squared;
var deltaX, deltaY;
var min_distance;
var seperation;
for (var i = 0; i < len; i++) {
for (var j = (i+1); j < len; j++) {
c1 = points[i];
c2 = points[j];
// try for trivial rejections first. Jury is still out if this will help
if (TrivialRejectIntesection(c1, c2)) {
continue;
}
//distance_squared is the actual distance between c1 and c2 'squared'
distance_squared = Distance_Squared(c1, c2);
// min_distance_squared is how much "squared distance" is required for these two circles to not intersect
min_distance_squared = (c1.radius_squared + c2.radius_squared + (c1.radius*c2.radius*2)); // D**2 == deltaX*deltaX + deltaY*deltaY + 2*deltaX*deltaY
// and so it follows
if (distance_squared < min_distance_squared) {
// intersection detected
// now subtract actual distance from "min distance"
seperation = c1.radius + c2.radius - Math.sqrt(distance_squared);
Console.log(c1, c2, seperation);
}
}
}
函数距离的平方(c1,c2){
增值税=(c1.x-c2.x);
变量deltaY=(c1.y-c2.y);
报税表(德尔泰*德尔泰+德尔泰*德尔泰);
}
//如果圆可能相交,则返回false。如果边界框测试证明没有相交机会,则返回true
函数求交(c1,c2){
return((c1.left>=c2.right)| |(c2.right=c2.bottom)| |(c2.bottom这篇文章已经休眠了很长一段时间,但我已经很好地遇到并解决了这个问题,所以我将发布文章,以便其他人不必做同样的挠头工作
可以将最近圆邻居问题视为kd树或八叉树中的3d点最近邻搜索。将两个圆a和B之间的距离定义为
D(A,B) = sqrt( (xA - xB)^2 + (yA - yB)^2 ) - rA - rB
当圆圈重叠时,这是一个负数。在本次讨论中,我将假设一个八叉树,但k=3的kd树是类似的
在八叉树中为每个圆存储一个三元组(x,y,r)
要找到目标圆T的最近邻点,请使用标准算法:
def search(node, T, nst)
if node is a leaf
update nst with node's (x,y,r) nearest to T
else
for each cuboid C subdividing node (there are 8 of them)
if C contains any point nearer to T than nst
search(C, T, nst)
end
end
这里的nst
是到目前为止找到的距离T最近的圆的引用。最初它是空的
稍微棘手的部分是确定<代码>如果C包含比NST 更接近T的任何点。为此,考虑唯一点(x,y,r)是足够的。在C中,在x和y中最接近T的欧几里德曲线,并且具有长方体中包含的r范围的最大值。换句话说,长方体表示一组圆心在x和y的矩形区域上,半径范围为的圆。要检查的点是表示圆心最接近T和y的圆的点具有最大的半径
注意,T的半径在算法中根本不起作用。你只关心T的中心在任何其他圆的内部有多远。(我希望这在一开始就像现在一样明显…看看这个答案可能值得研究。当然,这对实际的位置没有帮助。这也是一个“快速破解”是为了摆脱检查的sqrt
,因为sqrt(x)在此过程中进行了一些编辑。这可能是为了避免重复计算而进行的优化。还不错,但是如果两个圆之间的交点不是从左、右、上或下精确地相交会怎么样。如果交点发生在任何一个角点上,这将是无用的!!@似乎-我不知道圆有角点。谢谢!:/I没有我的意思是,如果相交发生在左上角、右上角、左下角或右下角,那就没用了。无论如何,谢谢你的回答:)