[英]Finding the squares in a plane given n points
给定平面上的n个点,可以形成多少个正方形......??
我通过计算每两个点之间的距离来尝试这个,然后对它们进行排序,并在验证点和斜率后寻找具有四个或更多相等距离的点中的正方形。
但这看起来是一种非常复杂的方法。 任何其他想法...??
我认为用于检查等距离线段的动态编程可能会起作用......但无法完全正确地理解这个想法......
有更好的主意吗???
PS:正方形可以是任何方式。 它们可以重叠,有共同的一面,一个正方形内另一个......
如果可能,请提供示例代码来执行上述操作...
让d[i][j] = distances between points i and j
。 我们对一个函数count(i, j)
感兴趣,它尽可能快地返回我们可以使用点i
和j
绘制的正方形数量。
基本上, count(i, j)
必须找到两个点x
和y
,使得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] ]
并计算该列表中与点i
和j
形成正方形的点。
这在实践中应该运行得非常快,我认为它不会比O(n^3)
更糟(我什至不确定它会变得那么糟糕)。
我有一个 O(N^2) 时间,O(N) 空间解决方案:
假设给定的点是一个对象点数组,每个点都有 x,y。
我在 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))
:
制作所有点的散列集。 可以用来快速检查点是否存在的东西。
按 X 坐标对点进行分组。 将超过sqrt(n)
个点的任何组分开,并通过它们的 Y 坐标重新组合那些现在空闲的点。 这保证了这些组最多有sqrt(n)
个点,并保证每个正方形都有一个具有两个正方形角点的组。
对于每一个组g
,为每对点p,q
在g
,检查包含两个可能正方形的其它两个点是否p
和q
都存在。 跟踪你找到了多少。 注意重复(两个相反的点是否也在一个组中?)。
为什么有效? 好吧,唯一棘手的是重组。 如果正方形的左列或右列位于不太大的组中,则在迭代该列组时将找到该正方形。 否则,它的两个左上角和右上角,得到重组,置于同一行组,并在该行组被反复方会被发现。
该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++ 中查找对角点的完整实现!
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.