Algorithm 在矩形中计数点

Algorithm 在矩形中计数点,algorithm,Algorithm,我有很多(数十亿)2D点可以预处理,我想回答以下形式的查询: 给定矩形的所有四个角,输出矩形内的点数 矩形可以是任意方向(这意味着矩形的轴可以是任意角度,而不仅仅是水平或垂直方向) 有没有一个快速实用的算法 更新。是否有任何数据结构来存储可证明查询处于次线性时间的点 更新II答案似乎是坚定的否定。在任何情况下都接受最流行的答案。您需要的是某种二进制空间分区数据结构。这将为您提供一个候选列表,您可以对其进行真正的“多边形中的点”测试 我建议您确保这是您真正应该自己编写的代码。例如,许多数据库都内置

我有很多(数十亿)2D点可以预处理,我想回答以下形式的查询:

给定矩形的所有四个角,输出矩形内的点数

矩形可以是任意方向(这意味着矩形的轴可以是任意角度,而不仅仅是水平或垂直方向)

有没有一个快速实用的算法

更新。是否有任何数据结构来存储可证明查询处于次线性时间的点


更新II答案似乎是坚定的否定。在任何情况下都接受最流行的答案。

您需要的是某种二进制空间分区数据结构。这将为您提供一个候选列表,您可以对其进行真正的“多边形中的点”测试

我建议您确保这是您真正应该自己编写的代码。例如,许多数据库都内置了这种功能。您的数据实际上是否驻留在数据库中?可以吗?(重新发明轮子毫无意义…)


您可以在这里看到多边形中的点问题的一个很好的答案:

我建议找到一个可以应用于空间的旋转+移位变换,这样矩形的一个角位于
(0,0)
中,两条边沿着
x
y

现在,您检查这些点,应用相同的转换,只需检查
0
0
旧答案(如果您无法预先预处理这些点):

  • 将矩形刻在包含矩形中,边/边的方向为xy轴
  • 快速排除它之外的所有点
  • 使用此处解释的原理:使用矩形的四条边(注意:由于您总是使用相同的矩形检查所有点,因此可以预先计算一些值)
通过快速包含保留在内接矩形中的点(边/边的方向为xy轴),可以在性能上获得一些东西(不多,这取决于矩形的方向)。这需要一些预先计算,但考虑到你有很多点,这是可以忽略的

新答案:

  • rect
    是我们的输入矩形
  • 假设您有
    f1(点,矩形)
    ,它检查点是否在矩形内。你可以用我上面提到的那个
  • 假设您有
    f2(shape,rect)
    ,这可以说明shape是否完全包含在rect中,或者rect是否完全包含在shape中,或者shape和rect是否相交
  • 形状将是具有一定数量边的多边形(不高或与n成比例,因此我们可以假设
    f2
    O(1)
    ),或2D平面中由两条边分隔并延伸到无限远的区域(例如,由xy轴正截面分隔的区域)
  • 假设您有很多时间来预处理点,但不是无限的。假设我们能负担得起一个O(n*log(n))算法
我们想要得到的是一个算法,在运行时调用f1和f2的时间最短。例如,与(相同顺序的)
log(n)

所以我们想把二维平面分成
m
形状,每个形状包含
p
点。在运行时,我们用f2检查每个形状,我们可以有4种情况:

  • 矩形完全包含在形状中:使用f1 I计数 此形状中包含的位于矩形中的所有点
    (O(p))
    ,我结束
  • 形状完全包含在图形中 矩形:我将点数加在累加器上 包含在形状中<代码>(O(1))
  • 矩形和形状不匹配 相交:我跳过这个形状
  • 矩形和形状相交: 使用f1,我计算此形状中包含的所有位于 矩形
    (O(p))
    ,我继续
  • 在案例1中,我们可以很幸运地快速下降,但通常我们必须检查所有形状,并且对于其中至少一个形状,我们必须检查包含的所有点。所以这个算法应该是
    O(p)+O(m)
    。考虑到
    p*m=n
    ,如果我们选择
    p=m=sqrt(n)
    我们得到
    O(sqrt(n))
    ,这是我们用这种方法能得到的最好结果。(注意:我们执行f1的次数?这个数字实际上取决于矩形的形状,因此,例如,如果矩形非常长,它将与许多区域相交,导致对f1的多次调用。但是,我认为我们可以假设矩形的度量值的顺序与
    n
    的顺序不同>sqrt(n)
    甚至
    log(n)
    n
    是巨大的。)

    我们可以从这里加强;例如,我们可以说形状之间有邻接,当我第一次发现形状和矩形之间有重叠时,我只检查相邻的形状。然而,我们必须检查的形状的平均数量将在p/2左右,并且
    O(p/2)=(O(p))
    。因此没有有效的收益

    真正的好处是,如果我们在形状中加入一些层次结构

    首先,我检查所有的点,找到我的界限值max_x,max_y,min_x,min_y。(假设这些边界为>>n。如果我们可以对点分布有先验知识,那么我们可以针对的优化将完全不同) 我们将我们的空间划分为形状,每个形状包含(大约)对数(n)个点。我们首先使用xy轴将2D平面划分为4个形状(我也可以根据边界值居中)。这将是我们倒置金字塔的第一层。 周期性地:对于每个连续的区域
    public class DataPoint
    {
        double X, Y;
        ...
    }
    public bool IsInBoundingBox(Point p1, Point p2, Point p3, Point p4)
    {
        // assume p1, p2, p3, p4 to be sorted
        return (X>p1.X && X<p3.X && Y>p4.Y && Y<p2.Y);
    }
    
    // sort points of QueryRectangle: p1 is left-most, p2 is top-most, p3 is right-
    // most, and p4 to be bottom-most; if there is a tie for left-most, p1 should
    // be the bottom-left corner, p2 the top-left corner, p3 the top-right corner,
    // and p4 the bottom-right corner
    
    // See if the QueryRectangle in question is aligned with the grid; if it is,
    // then the set of DataPoints that lie within the BoundingBox are within the
    // QueryRectangle and no further calculation is needed
    
    if (p1.X == p2.X || p1.X == p3.X || p1.X == p4.X) 
    {
        // is orthogonal (aligned with axes)
        foreach(DataPoint dp in listDataPoints)
            if(dp.IsInBoundingBox())
            {
                // dp is in QueryRectangle; perform work
            }
    }
    else
    {   
        foreach(DataPoint dp in listDataPoints)
            if(dp.IsInBoundingBox())
            {
                // perform further testing to see if dp is in QueryRectangle
            }
    }
    
    // sort points of QueryRectangle: p1 is left-most, p2 is top-most, p3 is right-
    // most, and p4 to be bottom-most; if there is a tie for left-most, p1 should
    // be the bottom-left corner, p2 the top-left corner, p3 the top-right corner,
    // and p4 the bottom-right corner
    
    public class DataPointList : List<DataPoint>
    {
        public List<DataPoint> GetPointsInRectangle(Point p1, Point p2, Point p3, Point p4)
        {
            List<DataPoint> tempListDataPoints = new List<DataPoint>();
            foreach(DataPoint dp in this)
                if(dp.IsInBoundingBox()) tempListDataPoints.Add(dp);
    
            if (!(p1.X == p2.X || p1.X == p3.X || p1.X == p4.X))
            {
                // needs transformation
                tempListDataPoints.TranslateAll(-1 * p1.X, -1 * p1.Y);
                tempListDataPoints.RotateAll(/* someAngle derived from the arctan of p1 and p2 */);
                // Note: you should be rotating counter-clockwise by some angle >0 but <90
    
                // the new p1 will be 0,0, but p2, p3, and p4 all need to undergo the same transformations
                // transP1 = new Point(0,0);
                // transP2 = new Point(p2.Translate(-1 * p1.X, -1 * p1.Y).Rotate(/* someAngle derived from the arctan of p1 and p2 */));
                // transP3 = ...; transP4 = ...;
    
                foreach(DataPoint dp in tempListDataPoints)
                    if (!(dp.X>transP1.X && dp.X<transP3.X && dp.Y>transP1.Y && dp.Y<transP3.Y)) tempListDataPoints.Remove(dp);
            }
            else
            {
                // QueryRectangle is aligned with axes, all points in bounding box
                // lie within the QueryRectangle, no need for transformation or any
                // further calculation
    
                // no code needs to go here, but you may want to keep it around for notes
            }
    
            return tempListDataPoints;
        }
    }
    
    A D a
    B E b
    C F c
    
    d = 1/(AE − BD)
    
    A' = Ed
    B' = −Bd
    C' = (BF − EC)d
    
    D' = −Dd
    E' = Ad
    F' = (DC − AF)d
    
    a' = b' = 0
    c' = 1
    
    |(P1.x, P1.y, 0)x(P2.x, P2.y, 0)[3]|+
    |(P2.x, P2.y, 0)x(P3.x, P3.y, 0)[3]|+
    |(P3.x, P3.y, 0)x(P4.x, P4.y, 0)[3]|+
    |(P4.x, P4.y, 0)x(P1.x, P1.y, 0)[3]|