【问题标题】:Finding all the points within a circle in 2D space在二维空间中查找圆内的所有点
【发布时间】:2016-04-15 17:16:38
【问题描述】:

我代表我的 2D 空间(考虑一个窗口),其中每个像素都显示为 2D 数组中的一个单元格。即 100x100 的窗口由相同尺寸的数组表示。

现在给定窗口中的一个点,如果我画一个半径为r 的圆,我想找到该圆中的所有点。

我想用side = 2*r 检查半径周围正方形区域中的每个点是否位于圆内。也许我会使用正常距离公式?

因此,可能如下:

for (x=center-radius ; x<center+radius ; x++){
    for (y=center-radius ; y<center+radius; y++) {
        if (inside) {
            // Do something
        }
    }
}

它会达到我的目的吗?我可以让它更快吗?

【问题讨论】:

  • 为了模拟 pi 数,在 xx+yy
  • 用毕达哥拉斯定理判断当前“细胞”的距离是否在圆内。

标签: arrays performance algorithm optimization geometry


【解决方案1】:

它会达到我的目的吗?

对于您的 100x100,是的。

我可以让它更快吗?

是的。例如,您可以:

  • 只检查 1 个象限,因为对称而得到其他点。
  • 计算距离时跳过平方根。

代码:

for (x = xCenter - radius ; x <= xCenter; x++)
{
    for (y = yCenter - radius ; y <= yCenter; y++)
    {
        // we don't have to take the square root, it's slow
        if ((x - xCenter)*(x - xCenter) + (y - yCenter)*(y - yCenter) <= r*r) 
        {
            xSym = xCenter - (x - xCenter);
            ySym = yCenter - (y - yCenter);
            // (x, y), (x, ySym), (xSym , y), (xSym, ySym) are in the circle
        }
    }
}

这大约是 4 倍的速度提升。

JS tests 获取此处提供的解决方案。 Symmetry 在我的电脑上是最快的。三角函数presented by Niet the Dark Absol 非常聪明,但它涉及到昂贵的数学函数,例如sinacos,这会对性能产生负面影响。

【讨论】:

  • 你说它适用于 100X100。它不适用于其他维度吗?
  • 它将始终有效。问题是,多快。在维度 d=2 中,它是 O(r^2),其中 r 是半径。在 d=3 中,它是 O(r^3),我们正在寻找球体中的点。通常它是 O(r^d)。您的问题是“它会达到我的目的吗”,我将其理解为“它会运行得如此之快以至于我感觉不到”。我说是的,因为对于现代计算机上的 r = 100 和 d = 2,即使在 JS 中,时间也不会被注意到。但是,对于更大的 r 或 d,时间会很明显。
  • 哦,对不起,我的意思是如果尺寸 100X100 是别的东西,即 500X300 或任何东西。
  • 是的,它会起作用的。速度与网格的大小无关。重要的是半径的大小。算法的复杂度是 O(r^d)。只有 r(半径)和 d(尺寸)很重要。
  • 作为一个 ES6 数组理解 [ /* (x, y) (-x, -y) are in circle */ for (x of r) for (y of r) if ((x - center)*(x - center) + (y - center)*(y - center) &lt; rad*rad)]; 在 FF 中试试
【解决方案2】:

您可以绕过条件检查:

for(x=center-radius; x<center+radius; x++) {
    yspan = radius*sin(acos((center-x)/radius));
    for(y=center-yspan; y<center+yspan; y++) {
        // (x,y) is inside the circle
    }
}

如果需要,可以round(yspan)

【讨论】:

  • 我假设,如果你有 x-center 和 y-center 的差异坐标,它会变为第一个 for loop: center = xcenter, yspan: center = xcenter 和第二个 for loop center = ycenter?
  • 是的。您甚至可以在forsin 中使用不同的radius 来获得椭圆。
  • @ChandrapalSinghJhala 直接在您的上方查看我的评论。
【解决方案3】:

您可以通过尽可能多地在循环之外进行计算来提高速度。也没有必要做毕达哥拉斯定理平方根......只要保持一切平方。最后一个加速可以通过只计算四分之一圆的数学来实现(因为它是对称的)......当找到匹配时,您只需将其复制到其他四分之三。

radiusSquared = radius*radius;
rightEdge = centerX+radius;
bottomEdge = centerY+radius;
for(x = centerX; x <= rightEdge; x++){
    xSquared = x*x;
    for(y = centerY; y <= bottomEdge; y++){
        ySquared = y*y;
        distSquared = xSquared+ySquared;
        if(distSquared <= radiusSquared){
            // Get positions for the other quadrants.
            otherX = centerX-(x-centerX);
            otherY = centerY-(y-centerY);
            // Do something for all four quadrants.
            doSomething(x, y);
            doSomething(x, otherY);
            doSomething(otherX, y);
            doSomething(otherX, otherY);
        }
    }
}

【讨论】:

    【解决方案4】:

    要获得一个圆圈内所有点的列表,您应该使用:

    var radius = 100, r2 = radius * radius;
    var circle = [];
    for (var dx = -radius; dx <= radius; dx++) {
      var h = Math.sqrt(r2 - dx * dx) | 0;
      for (var dy = -h; dy <= h; dy++) {
        circle.push([dx, dy])
      }
    }
    

    请参阅http://jsperf.com/circles/2,了解此处针对其他解决方案的分析。

    【讨论】:

      【解决方案5】:

      如果以下情况属实:

      ( ( xPos - centreX)^2 + (yPos - centreY)^2 ) <= radius^2
      

      其中xPosyPos 是您正在检查的点的坐标,那么该点就在您的圆圈内。

      【讨论】:

      【解决方案6】:

      似乎是正确的。您可以通过找到 minY 然后对当前 X 执行从 -rangeY 到 +rangeY 的 DoSomething 来稍微加快速度。

      for(dx=0;dx<rad; dx++)
      {
        rangeY = 0;
        while (!inside(x, rangeY)) //inside == check if x*x + y*y <r*r
          rangeY++;
      
        for(y=center-rangeY;y<center+rangeY;y++) 
        {
          DoSomething(centerX - dx, y);
          DoSomething(centerX + dx, y);      }
      }
      

      【讨论】:

        【解决方案7】:

        我知道这个问题有一个公认的答案,但我有一个更简单的解决方案。其他答案让我感到困惑,因为我不知道 centerxcenterycenter 是什么,而且函数背后的数学无法解释,我跋涉寻找自己的数学解决方案。

        我的方程式很简单:

        cx是圆心的x点

        cy是圆心的y点

        rad是圆的半径

        我的等式/函数所做的是通过计算给定半径的每个可能点来计算点,并添加和减去cxcy 的偏移量。

        //Creates an array filled with numbers
        function range(begin, end) {
            for (var i = begin, arr = []; i < end; i++) {
              arr.push(i);
            }
        
            return arr;
        }
        
        function calculateAllPointsInCircle(cx, cy, rad) {
        
           var rang = range(-rad, rad + 1);
           var px = [];
           var py = [];
           var xy = [];
        
           for (var i = 0; i < rang.length; i++) {
             var x = cx + rang[i];
             px.push(x);
        
             for (var l - rang.length - 1; l > 0; l--) {
                var y = cy + rang[l];
                if (!py.indexOf(y)===-1) { py.push(y); }
        
                xy.push(x+','+y);
             }
           }
        
           return {
             x: x,
             y: y,
             xy: xy
           }
        }
        

        性能远高于其他答案:http://jsperf.com/point-in-circle/4 你可以用数学检查我的等式,使用验证给定点是否在圆内的等式x*x + y*y &lt;= r*rx^2 + y^2 &lt;= r^2

        Edit-超压缩ES6版本:

        function range(begin, end) {
          for (let i = begin; i < end; ++i) {
            yield i;
          }
        }
        
        function calculateAllPointsInCircle(cx, cy, rad) {
            return {
                x: [cx + i for (i of range(-rad, rad + 1))],
                y: [cy + i for (i of range(-rad, rad + 1))]
            };
        }
        

        【讨论】:

        • 浪费了我的时间来检查它是否快速但不起作用
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        • 1970-01-01
        • 2022-01-05
        • 1970-01-01
        • 2013-03-22
        相关资源
        最近更新 更多