在给定n个点的平面中找到正方形

时间:2010-09-30 13:47:36

标签: c algorithm

给定一个平面中的n个点,可以形成多少个方格...... ??

我通过计算每个2点之间的距离,然后对它们进行排序,并在验证点和斜率后查找具有四个或更多相等距离的点中的方块来尝试此操作。

但这看起来像是一种复杂性很高的方法。任何其他想法...... ??

我认为用于检查相等距离的线段的动态编程可能会起作用......但是无法让这个想法完全正确....

任何更好的想法???

P.S:方块可以是任何方式。它们可以重叠,有一个共同的一面,一个正方形在另一个内...

任何有助于我更好地理解问题的链接都是可以理解的...如果可能的话,请提供示例代码来执行上述操作...

8 个答案:

答案 0 :(得分:3)

此问题可以O(n^1.5)时间O(n)空间解决。

基本思想是按X或Y坐标对点进行分组,小心避免使组太大。详情见文Finding squares and rectangles in sets of points。该论文还涵盖了许多其他案例(允许旋转的正方形,允许矩形,并在更高的维度工作)。

我已经解释了下面的2d轴对齐方形查找算法。请注意,我将其树集更改为哈希集,这就是为什么我给出的时间限制不是O(n^1.5 log(n))

  1. 制作所有点的哈希集。你可以用来快速检查一个点是否存在的东西。

  2. 按X坐标对点进行分组。打破分开sqrt(n)点以上的任何组,并按Y坐标重新分组这些现在可用的点。这可以保证群组最多只有sqrt(n)个点保证每个广场都有一个具有两个方角点的群组。

    < / LI>
  3. 对于每个群组g,对于p,q中的每对点g,检查包含p的两个可能正方形中的其他两个点是否为q {{1}}存在。跟踪您找到的数量。注意重复(在一组中也是两个相反的点?)。

  4. 为什么会这样?嗯,唯一棘手的事情就是重新组合。如果正方形的左列或右列都在不太大的组中,则在该列组被迭代时将找到正方形。否则 它的左上角和右上角会重新分组,放在同一个行组中,当该行组被迭代时,将找到方形。

答案 1 :(得分:1)

对我来说看起来像O(n ^ 3)。一个简单的算法可能是这样的:

for each pair of points
    for each of 3 possible squares which might be formed from these two points
        test remaining points to see if they coincide with the other two vertices

答案 2 :(得分:1)

d[i][j] = distances between points i and j。我们对函数count(i, j)感兴趣,该函数尽可能快地返回我们可以使用点ij绘制的正方形数。

基本上,count(i, j)必须找到xy两个点d[i][j] = d[x][y],并检查这4个点是否真正定义了一个正方形。

您可以使用hash table平均解决O(n^2)中的问题。让H[x] = list of all points (p, q) that have d[p][q] = x

现在,对于每对点(i, j)count(i, j)必须迭代H[ d[i][j] ]并计算该列表中形成带有点i和{的正方形的点数{1}}。

这应该在练习中运行得非常快,我认为它不会比j更糟(我甚至不确定它会变得那么糟糕)。

答案 3 :(得分:1)

This pdf包含相同的详细算法。

答案 4 :(得分:1)

我有一个O(N ^ 2)时间,O(N)空间解决方案:

假设给定的点是一个对象Point数组,每个Point都有x,y。

  1. 首先遍历数组并将每个项目添加到HashSet中:此操作会重复删除并为我们提供O(1)访问时间。整个过程需要O(N)时间
  2. 使用数学,假设顶点A,B,C,D可以形成正方形,AC是已知的并且它是对角线,那么相应的B,D是唯一的。我们可以写一个函数来计算它。这个过程是O(1)时间
  3. 现在让我们回到我们的事情。写一个for-i-loop和一个for-j-inner-loop。假设输入[i]和输入[j]形成对角线,在集合中找到它的反对角线:如果存在,则计数器++;此过程需要O(N ^ 2)时间。
  4. 我在C#中的代码:

        public int SquareCount(Point[] input)
        {
            int count = 0;
    
            HashSet<Point> set = new HashSet<Point>();
            foreach (var point in input)
                set.Add(point);
    
            for (int i = 0; i < input.Length; i++)
            {
                for (int j = 0; j < input.Length; j++)
                {
                    if (i == j)
                        continue;
                    //For each Point i, Point j, check if b&d exist in set.
                    Point[] DiagVertex = GetRestPints(input[i], input[j]);
                    if (set.Contains(DiagVertex[0]) && set.Contains(DiagVertex[1]))
                    {
                        count++;
                    }
                }
            }
            return count;
    
        }
    
        public Point[] GetRestPints(Point a, Point c)
        {
            Point[] res = new Point[2];
    
            int midX = (a.x + c.y) / 2;
            int midY = (a.y + c.y) / 2;
    
            int Ax = a.x - midX;
            int Ay = a.y - midY;
            int bX = midX - Ay;
            int bY = midY + Ax;
            Point b = new Point(bX,bY);
    
            int cX =  (c.x - midX);
            int cY =  (c.y - midY);
            int dX = midX - cY;
            int dY = midY + cX;
            Point d = new Point(dX,dY);
    
            res[0] = b;
            res[1] = d;
            return res;
        }
    

答案 5 :(得分:0)

只是一个想法:如果顶点A是正方形的一个角,则在其他角处必须有顶点B,C,D,AB = AD且AC = sqrt(2)AB和AC必须平分BD。假设每个顶点都有唯一的坐标,我认为你可以用O(n ^ 2)解决这个问题,其中哈希表键入(距离,角度)。

答案 6 :(得分:0)

Runtime: O(nlog(n)^2), Space: θ(n), where n is the number of points.

For each point p
Add it to the existing arrays sorted in the x and y-axis respectively.
  For every pair of points that collide with p in the x and y-axis respectively
   If there exists another point on the opposite side of p, increment square count by one.

直觉正在计算一个新点创建多少个正方形。所有正方形均在创建第四个点时创建。如果新点在相关轴上有任何碰撞点,并且在完成该正方形的相对侧上存在“第四”点,则该新点将创建一个新正方形。这将耗尽所有可能的不同正方形。

可以以二进制方式插入数组,并且可以通过访问散列点坐标的哈希表来检查相对点。

该算法最适合稀疏点,因为几乎没有要检查的碰撞点。与最佳理由相反,稠密正方形点是最优的。

通过跟踪轴阵列中的点在互补轴上是否存在碰撞,可以进一步优化该算法。

答案 7 :(得分:0)

这只是Java中的示例实现-欢迎任何评论。

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;


public class SweepingLine {


    public static void main(String[] args) {
        Point[] points = {
            new Point(1,1),
            new Point(1,4),
            new Point(4,1),
            new Point(4,4),
            new Point(7,1),
            new Point(7,4)
        };
        int max = Arrays.stream(points).mapToInt(p -> p.x).max().orElseThrow(NoSuchElementException::new);
        int count = countSquares(points, max);
        System.out.println(String.format("Found %d squares in %d x %d plane", count, max, max));
    }

    private static int countSquares(Point[] points, int max) {
        int count = 0;
        Map<Integer, List<Integer>> map = new HashMap<>();

        for (int x=0; x<max; x++) {
            for (int y=0; y<max; y++) {
                for(Point p: points) {
                    if (p.x == x && p.y == y) {
                        List<Integer> ys = map.computeIfAbsent(x, _u -> new ArrayList<Integer>());
                        ys.add(y);
                        Integer ley = null;
                        for (Integer ey: ys) {
                            if (ley != null) {
                                int d = ey - ley;
                                for (Point p2: points) {
                                    if (x + d == p2.x && p2.y == ey){
                                        count++;
                                    }
                                }
                            }
                            ley = ey;
                        }
                    }
                }
            }
        }
        return count;
    }

    private static class Point {
        public final int x;
        public final int y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

}