如何优化这个解决方案以避免超出时间限制?

huangapple go评论79阅读模式
英文:

How to optimize this solution to avoid exceeding the time limit?

问题

这是我对这个问题的解决方案:

给定一个表示为(半径,x中心,y中心)的圆和一个表示为(x1,y1,x2,y2)的轴对齐矩形,其中(x1,y1)是底部左下角的坐标,(x2,y2)是矩形的右上角的坐标。

如果圆和矩形重叠,则返回True,否则返回False。

换句话说,检查是否存在任何点(xi,yi)既属于圆又属于矩形。

class Solution {
    public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
       
        for(int i=x1; i<=x2; ){
            for(int j=y1; j<=y2; ){                
                System.out.println((Math.pow(i-x_center, 2) +" "+ Math.pow(j-y_center, 2))+" "+Math.pow(radius, 2));
                System.out.println(i+" "+j);
                if((Math.pow(i-x_center, 2)+Math.pow(j-y_center, 2))<=Math.pow(radius, 2)){
                    return true;
                }
                j += 1;
            }
            i += 1;
        }
        
        return false;
    }
}

我相当有信心这个逻辑是正确的。我从矩形的左下角到右上角的点逐步检查,对于每个点,我都检查它是否在圆内。
如果我将递增步长增加到超过 '1',我发现代码在矩形和圆刚好 '接触' 的测试用例中失败。但以这种方式进行会导致在某些情况下超出时间限制。我如何为这个逻辑优化代码?

提前感谢您的帮助。

英文:

This is my solution to this question:

> Given a circle represented as (radius, x_center, y_center) and an
> axis-aligned rectangle represented as (x1, y1, x2, y2), where (x1, y1)
> are the coordinates of the bottom-left corner, and (x2, y2) are the
> coordinates of the top-right corner of the rectangle.
>
> Return True if the circle and rectangle are overlapped otherwise
> return False.
>
> In other words, check if there are any point (xi, yi) such that
> belongs to the circle and the rectangle at the same time.

class Solution {
    public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
       
        for(int i=x1; i&lt;=x2; ){
            for(int j=y1; j&lt;=y2; ){                
                System.out.println((Math.pow(i-x_center, 2) +&quot; &quot;+ Math.pow(j-y_center, 2))+&quot; &quot;+Math.pow(radius, 2));
                System.out.println(i+&quot; &quot;+j);
                if((Math.pow(i-x_center, 2)+Math.pow(j-y_center, 2))&lt;=Math.pow(radius, 2)){
                    return true;
                }
                j += 1;
            }
            i += 1;
        }
        
        return false;
    }
}

I am pretty confident that the logic is correct. Ranging from the bottom left corner of the rectangle to the top right point, for every point, I am checking if it lies inside the circle.
If I increase the increment step to anything beyond '1', I see that the code fails for the test cases where the rectangle and circle just 'touch' each other. But having it this way leads to exceeding the time limit in some cases. How do I optimize the code for this logic?

Thanks in advance.

答案1

得分: 1

这个问题可以简化。我已经找到了一个时间复杂度为O(1) 并且 空间复杂度为O(1)的解决方案。不需要检查矩形中的每个像素,你甚至只需要考虑边界本身。

根据我的观察,有三种情况:

  1. 圆完全在矩形内部。
  2. 矩形完全在圆内部。
  3. 圆和矩形的轮廓至少在一个点交叉。这是较难的情况。

我将圆的中心坐标称为x0和y0。

  1. 你可以简单地检查圆的边界框,即圆的最北端点(x0,y0-radius),最南端点(x0,y0+radius),最东端(x0-radius,y0)和最西端(x0+radius,y0)。如果它们都在矩形内部,问题就解决了。

  2. 如果矩形完全在圆内部,这肯定意味着它的角落距离圆心的距离小于半径。只需为每个角落检查这个距离。

现在来看难点。

  1. 正如你已经找出的,位于圆内(或与之相交)意味着某些点必须距离圆心的距离小于等于半径。
    然而,对于矩形的检查部分,我们也可以进行优化。与矩形的交点可能意味着与构成矩形轮廓的至少一条边相交。因此,在矩形与圆相交的情况下,你需要检查是否有任何一条边与圆心的距离小于等于半径。

编辑:此外,你的代码在它们几乎接触的测试中失败的原因很可能是浮点错误。不要使用 ==(或在这种情况下是 <=,类似的)来检查两个浮点(甚至是浮点和整数)值是否相等。在Java中,Math.pow()返回双精度浮点数。对于平方,只需使用普通乘法即可。
事实上,除非你无法摆脱浮点数并且问题说明“允许0.001的误差”或类似情况,否则最好尽量避免使用浮点数。它们既慢又容易出错。

编辑2:此外,我已经编写了代码,以帮助你理解这个解释。我在网站上对其进行了测试,在每个运行时1毫秒且内存使用量为37.7MB的测试中都能正常工作。

class Solution {
    
    public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
        //case 1: circle is fully inside rectangle
        //check the bounding box of the circle against the rectangle
        if(x_center-radius>=x1&&y_center-radius>=y1
         &&x_center+radius<=x2&&y_center+radius<=y2
          )
            return true;
        //case 2: checking closest corner against circle bounds
        int minX=(Math.abs(x_center-x1)>Math.abs(x_center-x2))?(x_center-x2):(x_center-x1);
        int minY=(Math.abs(y_center-y1)>Math.abs(y_center-y2))?(y_center-y2):(y_center-y1);
        if(minX*minX+minY*minY<=radius*radius)
            return true;
        //case 3: checking distances to segments against circle bounds
        //Checking distance from a segment to a point is alike checking
        //the distance from the line the segment is part of to a point,
        //except you have to check if the closest point from the segment
        //is actually on the segment. If it isn't, the distance from a
        //segment to a point is the minimum distance from one of its
        //corners to the point.
        if(x1<=x_center&&x_center<=x2)
            if(minY*minY<=radius*radius)
                return true;
        if(y1<=y_center&&y_center<=y2)
            if(minX*minX<=radius*radius)
                return true;
        return false;
    }
}
英文:

This problem can be simplified. I've found a solution of time complexity O(1) and memory complexity O(1). Instead of checking for every single pixel from the rectangle, you can even only take into consideration the bounds themselves.

As I see it, there are 3 cases:

  1. Circle is fully inside rectangle.
  2. Rectangle is fully inside circle
  3. Circle and rectangle outlines intersect in at least one point. This is the tougher one.

I'll call center of the circle coordinates x0 and y0.

  1. You can simply check the bounding box for the circle, as in, which is the northern most point of the circle(x0,y0-radius), which is the southern-most point of the circle(x0,y0+radius), eastern-most(x0-radius,y0) and western-most(x0+radius,y0). If they all fall within the rectangle, then problem solved.

  2. If a rectangle is fully within a circle, that definitely means that its corners are at a smaller distance from the center of the circle than the radius. Simply check this distance for each corner.

Now comes the hard part.

  1. So, as you have figured out, being in a circle(or intersecting one) means that some point must be at a smaller or equal distance from the center than the radius.
    However, we can do an optimization for the rectangle checking part, too. Intersection with a rectangle likely means intersection with at least one of the segments that make the outline of the rectangle. So, in the case of an intersection between a rectangle and a circle, you need to check if there is any segment that would be at a smaller or equal distance from the center of the circle than the radius.

Edit: Also, the reason that your code fails on tests where they barely touch is likely due to floating-point errors. Do not use == (or in this case <=, which is similar) for checking if two floating point(or even a floating-point and integer) values are the same. Math.pow() in Java returns double. Just use normal multiplication for squaring.
In fact, you might want to stay as far from floating-point as possible, unless you can't figure out how to get rid of them and the problem says "0.001 error is acceptable" or something around the lines. They are both slow and prone to errors.

Edit 2: Also, I've written the code to help you aid in understanding this explanation. I've tested it on the site, it works for every test with runtime 1ms and memory usage 37.7Mb.

class Solution {
    
    public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
        //case 1: circle is fully inside rectangle
        //check the bounding box of the circle against the rectangle
        if(x_center-radius&gt;=x1&amp;&amp;y_center-radius&gt;=y1
         &amp;&amp;x_center+radius&lt;=x2&amp;&amp;y_center+radius&lt;=y2
          )
            return true;
        //case 2: checking closest corner against circle bounds
        int minX=(Math.abs(x_center-x1)&gt;Math.abs(x_center-x2))?(x_center-x2):(x_center-x1);
        int minY=(Math.abs(y_center-y1)&gt;Math.abs(y_center-y2))?(y_center-y2):(y_center-y1);
        if(minX*minX+minY*minY&lt;=radius*radius)
            return true;
        //case 3: checking distances to segments against circle bounds
        //Checking distance from a segment to a point is alike checking
        //the distance from the line the segment is part of to a point,
        //except you have to check if the closest point from the segment
        //is actually on the segment. If it isn&#39;t, the distance from a
        //segment to a point is the minimum distance from one of its
        //corners to the point.
        if(x1&lt;=x_center&amp;&amp;x_center&lt;=x2)
            if(minY*minY&lt;=radius*radius)
                return true;
        if(y1&lt;=y_center&amp;&amp;y_center&lt;=y2)
            if(minX*minX&lt;=radius*radius)
                return true;
        return false;
    }
}

This code could be possibly shortened. However, time complexity-wise, it can't get better than O(1).

huangapple
  • 本文由 发表于 2020年4月5日 05:14:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/61034887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定