【问题标题】:find if 4 points on a plane form a rectangle?找出平面上的4个点是否形成一个矩形?
【发布时间】:2011-01-19 04:25:26
【问题描述】:

有人可以用 C 风格的伪代码告诉我如何编写一个函数(表示你喜欢的点),如果 4 点(函数的参数)形成一个矩形,则返回 true,否则返回 false?

我想出了一个解决方案,首先尝试找到 2 对具有相同 x 值的不同点,然后对 y 轴执行此操作。但是代码比较长。只是想看看其他人的想法。

【问题讨论】:

  • 您想出了解决方案吗?它在哪里?你可以在这里展示它,我们可以帮助你让它更短更简洁。
  • 有趣的问题。我注意到您的解决方案只有在矩形与轴平行时才有效。
  • Gman - 是的,任何顺序。米兰 - 这是在一次采访中问我的,所以我没有我的代码(我不一定需要看代码..算法也会很棒!)。 Christian - 关于它必须与轴平行的好处。

标签: c algorithm geometry


【解决方案1】:
  • 求角点的质心:cx=(x1+x2+x3+x4)/4, cy=(y1+y2+y3+y4)/4
  • 测试从质心到所有 4 个角的距离的平方是否相等
bool isRectangle(double x1, double y1,
                 double x2, double y2,
                 double x3, double y3,
                 double x4, double y4)
{
  double cx,cy;
  double dd1,dd2,dd3,dd4;

  cx=(x1+x2+x3+x4)/4;
  cy=(y1+y2+y3+y4)/4;

  dd1=sqr(cx-x1)+sqr(cy-y1);
  dd2=sqr(cx-x2)+sqr(cy-y2);
  dd3=sqr(cx-x3)+sqr(cy-y3);
  dd4=sqr(cx-x4)+sqr(cy-y4);
  return dd1==dd2 && dd1==dd3 && dd1==dd4;
}

(当然在实践中测试两个浮点数 a 和 b 的相等性应该以有限的精度进行:例如 abs(a-b)

【讨论】:

  • 这是一个聪明的解决方案。它基本上找到了“矩形”的外接圆,并验证所有四个角都在上面。
  • 这是非常低效的。使用 Vlad 提供的点积方法。平方根需要数百个时钟周期。此外,点积法在数值上更稳定。
  • @Axel & @Curd:自原始发布以来,解决方案是否已编辑?我没有看到任何平方根。我假设sqr(x) == x*x 这意味着ddi 实际上是从cxxi 的距离的平方。这应该很快。
  • 好吧,那我要道歉了。我认为 sqr 代表平方根。
  • 一些关于性能的考虑:这个解决方案需要 20 次加法/减法/除法,乘以常数 4 和 8 次乘法。如果第一次或第二次比较失败,它甚至可以优化以放弃剩余的平方距离计算。所以上面的数字是最坏的情况。即使是这种最坏的情况也与最好的情况一样好,比 Vlad 的解决方案的最坏情况好 3 倍。
【解决方案2】:
struct point
{
    int x, y;
}

// tests if angle abc is a right angle
int IsOrthogonal(point a, point b, point c)
{
    return (b.x - a.x) * (b.x - c.x) + (b.y - a.y) * (b.y - c.y) == 0;
}

int IsRectangle(point a, point b, point c, point d)
{
    return
        IsOrthogonal(a, b, c) &&
        IsOrthogonal(b, c, d) &&
        IsOrthogonal(c, d, a);
}

如果事先不知道订单,我们需要稍微复杂一点的检查:

int IsRectangleAnyOrder(point a, point b, point c, point d)
{
    return IsRectangle(a, b, c, d) ||
           IsRectangle(b, c, a, d) ||
           IsRectangle(c, a, b, d);
}

【讨论】:

  • @Larry: 你的测试只是为了成为一个平行四边形
  • @Larry:现在检查对角线是正确的,但是您的测试需要取 6 个平方根。
  • @Timmy:在这种情况下,需要进行更复杂的检查:if (IsOrthogonal(a, b, c)) return IsRectangle(a, b, c, d); else if (IsOrthogonal(a, b, d)) return IsRectangle(a, b, d, c); else return false;
  • 我相应地修改了答案
  • IsOrthogonal( (10,9), (15,9), (15,6) ) = 0 或 False。 (15-10)*(15-15)+(9-9)*(9-6)=0。有没有 !不见了?
【解决方案3】:
  • 平移四边形,使其一个顶点现在位于原点
  • 剩下的三个点从原点形成三个向量
  • 其中一个必须代表对角线
  • 另外两个必须代表边
  • parallelogram rule 如果边形成对角线,我们有一个平行四边形
  • 如果边形成直角,则为直角平行四边形
  • 平行四边形的对角相等
  • 平行四边形的连续角是互补的
  • 因此所有角度都是直角
  • 它是一个矩形
  • 虽然代码更简洁:-)

    static bool IsRectangle(
       int x1, int y1, int x2, int y2, 
       int x3, int y3, int x4, int y4)
    {
        x2 -= x1; x3 -= x1; x4 -= x1; y2 -= y1; y3 -= y1; y4 -= y1;
        return
            (x2 + x3 == x4 && y2 + y3 == y4 && x2 * x3 == -y2 * y3) ||
            (x2 + x4 == x3 && y2 + y4 == y3 && x2 * x4 == -y2 * y4) ||
            (x3 + x4 == x2 && y3 + y4 == y2 && x3 * x4 == -y3 * y4);
    }
    
  • (如果你想让它使用浮点值,请不要盲目地替换标题中的 int 声明。这是不好的做法。它们存在原因。在比较浮点结果时,应该始终使用一些错误上限。)

【讨论】:

  • 最坏情况:15 次加法/减法,6 次乘法。
【解决方案4】:
1. Find all possible distances between given 4 points. (we will have 6 distances)
2. XOR all distances found in step #1
3. If the result after XORing is 0 then given 4 points are definitely vertices of a square or a rectangle otherwise, return false (given 4 points do not form a rectangle).
4. Now, to differentiate between square and rectangle 
   a. Find the largest distance out of 4 distances found in step #1. 
   b. Check if the largest distance / Math.sqrt (2) is equal to any other distance.
   c. If answer is No, then given four points form a rectangle otherwise they form a square.

在这里,我们使用矩形/正方形Bit Magic的几何属性。

正在使用的矩形属性

  1. 矩形的对边和对角线等长。
  2. 如果矩形的对角线长度是其任意长度的 sqrt(2) 倍,则该矩形是正方形。

位魔法

  1. 异或等值数字返回 0。

由于矩形的 4 个角之间的距离总是形成 3 对,一对用于对角线,两对用于不同长度的边,异或所有值将返回矩形的 0。

【讨论】:

  • 很酷的想法,但如果您需要以较小的容差测试相等性以考虑浮点精度,则可能不切实际。还可能值得补充的是,xor 技巧有效,因为 xor 是可交换和关联的
  • 4.b。为什么不检查 4 个距离是否相等?
【解决方案5】:

如果点是 A、B、C 和 D,并且您知道顺序,那么您可以计算向量:

x=B-A、y=C-B、z=D-C 和 w=A-D

然后取点积(x dot y)、(y dot z)、(z dot w) 和(w dot x)。如果它们都为零,那么你就有一个矩形。

【讨论】:

  • 如果你知道顺序,然后检查 |x| = |z|, |y| = |w|一个点积就足够了。 (因为对边的长度必须相等,那么对边长度相等的四边形也不少。)
【解决方案6】:

一个点到另外三个点的距离应该是一个直角三角形:

| / /| | // | | // | |/___ /___|
d1 = sqrt( (x2-x1)^2 + (y2-y1)^2 ) 
d2 = sqrt( (x3-x1)^2 + (y3-y1)^2 ) 
d3 = sqrt( (x4-x1)^2 + (y4-y1)^2 ) 
if d1^2 == d2^2 + d3^2 then it's a rectangle

简化:

d1 = (x2-x1)^2 + (y2-y1)^2
d2 = (x3-x1)^2 + (y3-y1)^2
d3 = (x4-x1)^2 + (y4-y1)^2
if d1 == d2+d3 or d2 == d1+d3 or d3 == d1+d2 then return true

【讨论】:

  • @andras - 测试了几个平行四边形,结果都被评估为假。你在考虑一个特定的案例吗?
  • 假设我们有点 x1=3, y1=3; x2=0,y2=0; x3=6,y3=0; x4=9,y4=3;现在 d1 = 18; d2 = 18; d3 = 36;不过,已经晚了。 :-) 请检查一下好吗?
  • @andras - 你是对的,看起来必须使用 3 个点作为起点重复测试。
  • 拜托,那就做点什么吧。
  • 这是错误的,最后一行必须是d1^2 == d2^2+d3^2 or d2^2 == d1^2 + d3^2 or d3^2 == d1^ 2 + d2 ^2
【解决方案7】:

如果两条直线的斜率为-1,我们知道两条直线是垂直的,因为给定了一个平面,我们可以找到三个连续直线的斜率,然后将它们相乘以检查它们是否真的垂直。假设我们有 L1、L2、L3 行。现在如果 L1 垂直于 L2 并且 L2 垂直于 L3,那么它是一个矩形,并且 m(L1)*m(L2)=-1 和 m(L2)*m(L3)=-1 的斜率,那么它暗示它是一个矩形。代码如下

bool isRectangle(double x1,double y1,
        double x2,double y2,
        double x3,double y3,
        double x4,double y4){
    double m1,m2,m3;
    m1 = (y2-y1)/(x2-x1);
    m2 = (y2-y3)/(x2-x3);
    m3 = (y4-y3)/(x4-x3);

    if((m1*m2)==-1 && (m2*m3)==-1)
        return true;
    else
        return false;
}

【讨论】:

  • 我认为这是计算效率最高的。
  • 你也应该检查 m4,否则你可能会得到一个梯形。
【解决方案8】:

更进一步的点积建议,检查点的任意 3 个点组成的向量中的两个是否垂直,然后查看 x 和 y 是否与第四个点匹配。

如果你有点 [Ax,Ay] [Bx,By] [Cx,Cy] [Dx,Dy]

向量 v = B-A 向量 u = C-A

v(点)u/|v||u| == cos(θ)

所以如果 (v.u == 0) 那里有几条垂直线。

我实际上不懂 C 编程,但这里有一些“元”编程给你:P

if (v==[0,0] || u==[0,0] || u==v || D==A) {not a rectangle, not even a quadrilateral}

var dot = (v1*u1 + v2*u2); //computes the "top half" of (v.u/|v||u|)
if (dot == 0) { //potentially a rectangle if true

    if (Dy==By && Dx==Cx){
     is a rectangle
    }

    else if (Dx==Bx && Dy==Cy){
     is a rectangle
    }
}
else {not a rectangle}

其中没有平方根,也没有被零除的可能性。我注意到人们在之前的帖子中提到了这些问题,所以我想我会提供一个替代方案。

因此,在计算上,您需要四次减法来得到 v 和 u,两次乘法,一次加法,并且您必须检查 1 到 7 之间的某个等式。

也许我正在编造这个,但我隐约记得在某处读到减法和乘法是“更快”的计算。我假设声明变量/数组并设置它们的值也很快?

抱歉,我对这种事情还很陌生,所以我很想对我刚刚写的内容提供一些反馈。

编辑:根据我下面的评论试试这个:

A = [a1,a2];
B = [b1,b2];
C = [c1,c2];
D = [d1,d2];

u = (b1-a1,b2-a2);
v = (c1-a1,c2-a2);

if ( u==0 || v==0 || A==D || u==v)
    {!rectangle} // get the obvious out of the way

var dot = u1*v1 + u2*v2;
var pgram = [a1+u1+v1,a2+u2+v2]
if (dot == 0 && pgram == D) {rectangle} // will be true 50% of the time if rectangle
else if (pgram == D) {
    w = [d1-a1,d2-a2];

    if (w1*u1 + w2*u2 == 0) {rectangle} //25% chance
    else if (w1*v1 + w2*v2 == 0) {rectangle} //25% chance

    else {!rectangle}
}
else {!rectangle}

【讨论】:

  • 啊,我刚刚意识到这有平行于轴的问题。因此,它应该测试 if (D == A + v + u),而不是最后的 if 语句。我还注意到,如果您将对角线作为前 3 个点之一,它可能会给出假阴性,因此如果点积失败,它应该将 u 重新定义为 AD 并重试。
【解决方案9】:

我最近遇到了类似的挑战,但是在 Python 中,这是我在 Python 中提出的,也许这种方法可能很有价值。这个想法是有六条线,如果创建成一个集合,应该剩下 3 个唯一的线距离 - 长度、宽度和对角线。

def con_rec(a,b,c,d): 
        d1 = a.distanceFromPoint(b)
        d2 = b.distanceFromPoint(c)
        d3 = c.distanceFromPoint(d)
        d4 = d.distanceFromPoint(a)
        d5 = d.distanceFromPoint(b)
        d6 = a.distanceFromPoint(c)
        lst = [d1,d2,d3,d4,d5,d6] # list of all combinations 
        of point to point distances
        if min(lst) * math.sqrt(2) == max(lst): # this confirms a square, not a rectangle
            return False
        z = set(lst) # set of unique values in ck
        if len(lst) == 3: # there should be three values, length, width, diagonal, if a 
        4th, it's not a rectangle
            return True
        else: # not a rectangle
            return False

【讨论】:

    【解决方案10】:

    如何验证这4个点是否可以形成一个平行四边形,然后找出是否存在一个直角
    1.验证平行四边形

    input 4 points A, B, C, D;

    if(A, B, C, D are the same points), exit;// not a rectangle;

    else form 3 vectors, AB, AC, AD, verify(AB=AC+AD || AC=AB+AD || AD=AB+AC), \\if one of them satisfied, this is a parallelogram;

    2.验证直角

    through the last step, we could find which two points are the adjacent points of A;

    We need to find out if angle A is a right angle, if it is, then rectangle.

    我不知道是否存在错误。有的话请自行判断。

    【讨论】:

    • 请确保您的回答实际上是对 OP 问题的回答。他问如何找出点是否形成矩形,而不是它们是否形成平行四边形。另外,一个直角也不能保证是一个矩形。
    • 嗯,一个直角平行四边形就是一个长方形,所以我相信上面两步可以验证。
    • 很抱歉,我的想法不同。
    【解决方案11】:

    这是我的算法建议,用于轴对齐矩形测试,但在 Python 中。

    这个想法是将第一个点作为枢轴,并且所有其他点必须符合相同的宽度和高度,并通过一个集合检查所有点是否不同,以解决诸如 (1 , 2), (1, 2), (10, 30), (10, 30)。

    from collections import namedtuple
    
    Point = namedtuple('Point', ('x', 'y'))
    
    def is_rectangle(p1, p2, p3, p4) -> bool:
        width = None
        height = None
    
        # All must be distinct
        if (len(set((p1, p2, p3, p4))) < 4):
            return False
    
        pivot = p1
    
        for point in (p2, p3, p4):
            candidate_width = point.x - pivot.x
            candidate_height = point.y - pivot.y
    
            if (candidate_width != 0):
                if (width is None):
                    width = candidate_width
                elif (width != candidate_width):
                    return False
    
            if (candidate_height != 0):
                if (height is None):
                    height = candidate_height
                elif (height != candidate_height):
                    return False
    
        return width is not None and height is not None
    
    # Some Examples
    print(is_rectangle(Point(10, 50), Point(20, 50), Point(10, 40), Point(20, 40)))
    print(is_rectangle(Point(100, 50), Point(20, 50), Point(10, 40), Point(20, 40)))
    print(is_rectangle(Point(10, 10), Point(20, 50), Point(10, 40), Point(20, 40)))
    print(is_rectangle(Point(10, 30), Point(20, 30), Point(10, 30), Point(20, 30)))
    print(is_rectangle(Point(10, 30), Point(10, 30), Point(10, 30), Point(10, 30)))
    print(is_rectangle(Point(1, 2), Point(10, 30), Point(1, 2), Point(10, 30)))
    print(is_rectangle(Point(10, 50), Point(80, 50), Point(10, 40), Point(80, 40)))
    

    【讨论】:

    • 这似乎假设 axis-aligned rectangle (正如问题所暗示的那样,但没有明确指定)。 (如果是等向的,为什么要四个点作为参数?)
    • @greybeard 4 分作为参数提供,正如问题中所要求的那样。是的,这里的假设是它正在检查轴对齐的矩形。我会用那句话更新答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多