繁体   English   中英

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

[英]Finding the squares in a plane given n points

给定平面上的n个点,可以形成多少个正方形......??

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

但这看起来是一种非常复杂的方法。 任何其他想法...??

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

有更好的主意吗???

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

如果可能,请提供示例代码来执行上述操作...

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

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

您可以使用哈希表来解决平均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] ]并计算该列表中与点ij形成正方形的点。

这在实践中应该运行得非常快,我认为它不会比O(n^3)更糟(我什至不确定它会变得那么糟糕)。

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

假设给定的点是一个对象点数组,每个点都有 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。 说 input[i] 和 input[j] 形成一条对角线,在集合中找到它的对角线与否:如果存在,计数器++; 这个过程需要 O(N^2) 时间。

我在 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;
    }

这个问题可以用O(n)空间在O(n^1.5)时间内解决。

基本思想是按 X 或 Y 坐标对点进行分组,注意避免分组太大。 详细信息在论文Find squares and rectangles in sets of points 中 该论文还涵盖了许多其他情况(允许旋转正方形、允许矩形和在更高维度上工作)。

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

  1. 制作所有点的散列集。 可以用来快速检查点是否存在的东西。

  2. 按 X 坐标对点进行分组。 将超过sqrt(n)个点的任何组分开,并通过它们的 Y 坐标重新组合那些现在空闲的点。 这保证了这些组最多有sqrt(n)个点,保证每个正方形都有一个具有两个正方形角点的组。

  3. 对于每一个组g ,为每对点p,qg ,检查包含两个可能正方形的其它两个点是否pq都存在。 跟踪你找到了多少。 注意重复(两个相反的点是否也在一个组中?)。

为什么有效? 好吧,唯一棘手的是重组。 如果正方形的左列或右列位于不太大的组中,则在迭代该列组时将找到该正方形。 否则,它的两个左上角和右上角,得到重组,置于同一行组,并在该行组被反复方会被发现。

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

对我来说看起来像 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

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

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.

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

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

该算法最适用于稀疏点,因为要检查的碰撞点非常少。 与最优的原因相反,对于密集的平方点是悲观的。

如果轴阵列中的点在互补轴上发生碰撞,可以通过跟踪来进一步优化该算法。

这只是 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;
        }
    }

}

这是在 C++ 中查找对角点的完整实现!

  • 给定点 a 和 c,返回位于对角线上的 b 和 d
  • 如果 b 或 d 不是 integer 点,则丢弃它们(可选)
  • 要查找由 n 个点生成的所有正方形,可以查看此C++ 实现
  • 想法归功于凯夫曼。 希望它可以帮助!
vector<vector<int>> createDiag(vector<int>& a, vector<int>& c){
    double midX = (a[0] + c[0])/2.0;
    double midY = (a[1] + c[1])/2.0;
    
    double bx = midX - (a[1] - midY);
    double by = midY + (a[0] - midX);
    double dx = midX - (c[1] - midY);
    double dy = midY + (c[0] - midX);
    
    // discard the non-integer points
    double intpart;
    if(modf(bx, &intpart) != 0 or modf(by, &intpart) != 0 or modf(dx, &intpart) != 0 or modf(dy, &intpart) != 0){
        return {{}};
    }
    return {{(int)bx, (int)by}, {(int)dx, (int)dy}};
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM