【问题标题】:Quad tree and Kd tree四叉树和Kd树
【发布时间】:2017-08-09 22:54:59
【问题描述】:

我有一组不同位置的经纬度,也知道我当前位置的经纬度。我必须找出离当前位置最近的地方。

  • Kdtree 和四叉树中哪种算法最好,可以从一组纬度和经度中找出相邻位置?
  • 一个比另一个有什么优势?
  • 为了上述目的,我们如何在 C# 中实现这些算法?

【问题讨论】:

    标签: c# geolocation nearest-neighbor kdtree quadtree


    【解决方案1】:

    有几个逻辑错误:

    for(i = 0; i <= numParentPoints-1; i++)
        return m;
    

    【讨论】:

    【解决方案2】:

    比较空间索引技术我想在我们的比较研究中引入第三种技术,称为网格索引。为了理解四叉树,我想先进入网格索引。

    什么是网格索引?

    网格索引是一种基于网格的空间索引方法,其中研究区域像棋盘一样被划分为固定大小的瓦片(固定尺寸)。

    使用网格索引,图块中的每个点都使用该图块编号进行标记,因此索引表可以为每个点提供一个标签,显示我们的编号所在的图块。

    想象一下您需要在给定矩形中找到点的情况。 此查询分两步执行:

    1. 查找矩形重叠的图块,以及图块中的点(第一个过滤器)
    2. 在上述步骤中找到实际位于我们矩形中的候选点。这需要使用点和矩形坐标准确地完成。(第二个过滤器)

    第一个过滤器创建一组候选者,并阻止测试我们研究区域中的所有点,以便一个接一个地检查。

    第二个过滤器是准确的检查,使用矩形坐标来测试候选人。

    现在,看看上面图片中的瓷砖,如果瓷砖非常大或非常小会怎样?

    当图块太大时,例如假设您有一块与您的学习区域大小相同的图块,则只能制作一个图块!所以第一个过滤器实际上是无用的,整个处理负载将由第二个过滤器负担。在这种情况下,第一个过滤器很快,第二个过滤器很慢。

    现在假设图块非常小,在这种情况下,第一个过滤器非常慢,实际上它自己生成答案,第二个过滤器很快。

    确定图块大小非常重要,直接影响性能,但如果无法确定最佳图块尺寸怎么办?如果您所在的区域既有备用分区又有密集分区怎么办? 现在是时候使用其他空间索引机制,如 R-Tree、KD-Tree 或 Quad-Tree!

    什么是四叉树?

    四叉树方法从一个覆盖整个研究区域的大图块开始,将其除以两条水平和垂直线,得到四个相等的区域,它们是新的图​​块,然后检查每个图块,看它是否有超过定义的阈值,其中的点。在这种情况下,瓷砖将再次使用水平和垂直分割线分成四个相等的部分。该过程一直持续到不再有点数大于阈值的图块,这是一种递归算法。

    因此,在较密集的区域,当有备用点时,我们有较小的瓷砖和大瓷砖。

    什么是KD-Tree? 在 KD-Tree 中,如果一个区域中包含多个阈值点(可以使用其他标准),我们将其划分为使用 (K-1) 维几何进行划分,例如在 3D-Tree 中我们需要一个平面划分空间,在二维树中,我们需要一条线来划分区域。 分割几何是迭代和循环的,例如在 3D-Tree 中,第一个分割平面是 X 轴对齐的平面,下一个分割平面是 Y 轴对齐的,下一个是 Z,循环继续每个空间部分变得可以接受(满足条件)

    下图展示了一个平衡的KD-Tree,每条分割线都是一个中值,将一个区域划分为两个点数大致相等的子区域。

    结论: 如果您有一个分布良好的点,而在地图中谈论地球的结构特征时并非如此,因为它们是随机的,但在我们计划存储城市道路网络时是可以接受的。我会选择 Grid 索引。

    如果您的资源有限(即汽车导航系统),您需要实施 KD-Tree 或 Quad-Tree。每个都有自己的优点和缺点。

    1. 四叉树创建了很多空的子图块,因为即使我们的图块的全部数据可以放在四分之一中,每个图块也必须分成四部分,因此其余子图块被认为是多余的。 (看看上面的四叉树图片)
    2. 四叉树的索引更简单,实现起来也更容易。使用 Tile ID 访问 tile 不需要递归函数。
    3. 在二维 Kd-Tree 中,每个节点只有两个子节点或根本没有子节点,因此通过 KD-Tree 搜索本质上是二分搜索
    4. 更新 Quad-Tree 比更新平衡的 KD-Tree 要容易得多。

    有了上面的描述,我建议从四叉树开始

    这是一个用于创建 5000 个随机点的四叉树示例代码。

    #include<stdio.h>
    #include<stdlib.h>
    //Removed windows-specific header and functions
    
    //-------------------------------------
    // STRUCTURES
    //-------------------------------------
    struct Point
    {
        int x;
        int y;
    };
    
    
    struct Node
    {
        int posX;
        int posY;
        int width;
        int height;
        Node *child[4];         //Changed to Node *child[4] rather than Node ** child[4]
        Point pointArray[5000];
    };
    //-------------------------------------
    // DEFINITIONS
    //-------------------------------------
    
    void BuildQuadTree(Node *n);
    void PrintQuadTree(Node *n, int depth = 0);
    void DeleteQuadTree(Node *n);
    Node *BuildNode(Node *n, Node  *nParent, int index);
    
    //-------------------------------------
    // FUNCTIONS
    //-------------------------------------
    
    void setnode(Node *xy,int x, int y, int w, int h)
    {
        int i;
        xy->posX = x;
        xy->posY = y;
        xy->width= w;
        xy->height= h;
        for(i=0;i<5000;i++)
        {
            xy->pointArray[i].x=560;
            xy->pointArray[i].y=560;
        }
        //Initialises child-nodes to NULL - better safe than sorry
        for (int i = 0; i < 4; i++)
            xy->child[i] = NULL;
    }
    int randn()
    {
        int a;
        a=rand()%501;
        return a;
    }
    
    int pointArray_size(Node *n)
    {
        int m = 0,i;
        for (i = 0;i<=5000; i++)
            if(n->pointArray[i].x <= 500 && n->pointArray[i].y <= 500)
                m++;
        return (m + 1);
    }
    //-------------------------------------
    // MAIN
    //-------------------------------------
    int main()
    {
        // Initialize the root node
        Node * rootNode = new Node;     //Initialised node
        int i, x[5000],y[5000];
        FILE *fp;
        setnode(rootNode,0, 0, 500, 500);
    
    
        // WRITE THE RANDOM POINT FILE  
        fp = fopen("POINT.C","w");
    
        if ( fp == NULL )
        {
            puts ( "Cannot open file" );
            exit(1);
        }
        for(i=0;i<5000;i++)
        {
            x[i]=randn();
            y[i]=randn();
            fprintf(fp,"%d,%d\n",x[i],y[i]);
        }
        fclose(fp);
    
        // READ THE RANDOM POINT FILE AND ASSIGN TO ROOT Node
        fp=fopen("POINT.C","r");
        for(i=0;i<5000;i++)
        {
            if(fscanf(fp,"%d,%d",&x[i],&y[i]) != EOF)
            {
                rootNode->pointArray[i].x=x[i];
                rootNode->pointArray[i].y=y[i];
            }
        }
    
        fclose(fp);
    
        // Create the quadTree
        BuildQuadTree(rootNode);
        PrintQuadTree(rootNode);    //Added function to print for easier debugging
        DeleteQuadTree(rootNode);
    
        return 0;
    }
    
    //-------------------------------------
    // BUILD QUAD TREE
    //-------------------------------------
    void BuildQuadTree(Node *n)
    {
        Node * nodeIn = new Node;   //Initialised node
    
        int points = pointArray_size(n);
    
        if(points > 100)
        {
            for(int k =0; k < 4; k++)
            {
                n->child[k] = new Node;     //Initialised node
                nodeIn = BuildNode(n->child[k], n, k);
                BuildQuadTree(nodeIn);
            }
        }
    }
    //-------------------------------------
    // PRINT QUAD TREE
    //-------------------------------------
    void PrintQuadTree(Node *n, int depth)
    {
        for (int i = 0; i < depth; i++)
            printf("\t");
    
        if (n->child[0] == NULL)
        {
            int points = pointArray_size(n);
            printf("Points: %d\n", points);
            return;
        }
        else if (n->child[0] != NULL)
        {
            printf("Children:\n");
            for (int i = 0; i < 4; i++)
                PrintQuadTree(n->child[i], depth + 1);
            return;
        }
    }
    //-------------------------------------
    // DELETE QUAD TREE
    //-------------------------------------
    void DeleteQuadTree(Node *n)
    {
        if (n->child[0] == NULL)
        {
            delete n;
            return;
        }
        else if (n->child[0] != NULL)
        {
            for (int i = 0; i < 4; i++)
                DeleteQuadTree(n->child[i]);
            return;
        }
    }
    //-------------------------------------
    // BUILD NODE
    //-------------------------------------
    Node *BuildNode(Node *n, Node *nParent, int index)
    {
        int numParentPoints, i,j = 0;
    
        // 1) Creates the bounding box for the node
        // 2) Determines which points lie within the box
    
        /*
         Position of the child node, based on index (0-3), is determined in this order:
         | 1 | 0 |
         | 2 | 3 |
         */
    
        setnode(n, 0, 0, 0, 0);
    
        switch(index)
        {
            case 0: // NE
    
                n->posX = nParent->posX+nParent->width/2;
                n->posY = nParent->posY+nParent->height/2;
                break;
    
            case 1: // NW
    
                n->posX = nParent->posX;
                n->posY = nParent->posY+nParent->height/2;
                break;
    
            case 2: // SW
    
                n->posX = nParent->posX;
                n->posY = nParent->posY;
                break;
    
            case 3: // SE
    
                n->posX = nParent->posX+nParent->width/2;
                n->posY = nParent->posY;
                break;
    
        }
    
        // Width and height of the child node is simply 1/2 of the parent node's width and height
        n->width = nParent->width/2;
        n->height = nParent->height/2;
    
        // The points within the child node are also based on the index, similiarily to the position
        numParentPoints = pointArray_size(nParent);
    
        switch(index)
        {
            case 0: // NE
                for(i = 0; i < numParentPoints-1; i++)
                {
                    // Check all parent points and determine if it is in the top right quadrant
                    if(nParent->pointArray[i].x<=500 && nParent->pointArray[i].x > nParent->posX+nParent->width/2 && nParent->pointArray[i].y > nParent->posY + nParent->height/2 && nParent->pointArray[i].x <= nParent->posX + nParent->width && nParent->pointArray[i].y <= nParent->posY + nParent-> height)
                    {
                        // Add the point to the child node's point array
                        n->pointArray[j].x = nParent ->pointArray[i].x;
                        n->pointArray[j].y = nParent ->pointArray[i].y;
                        j++;
                    }
                }
                break;
            case 1: // NW
                for(i = 0; i < numParentPoints-1; i++)
                {
                    // Check all parent points and determine if it is in the top left quadrant
                    if(nParent->pointArray[i].x<=500 && nParent->pointArray[i].x > nParent->posX && nParent->pointArray[i].y > nParent->posY+ nParent-> height/2 && nParent->pointArray[i].x <= nParent->posX + nParent->width/2 && nParent->pointArray[i].y <= nParent->posY + nParent->height)
                    {
                        // Add the point to the child node's point array
                        n->pointArray[j].x = nParent ->pointArray[i].x;
                        n->pointArray[j].y = nParent ->pointArray[i].y;
                        j++;
                    }
                } 
                break;
            case 2: // SW
                for(i = 0; i < numParentPoints-1; i++)
                {
                    // Check all parent points and determine if it is in the bottom left quadrant
                    if(nParent->pointArray[i].x<=500 && nParent->pointArray[i].x > nParent->posX && nParent->pointArray[i].y > nParent->posY && nParent->pointArray[i].x <= nParent->posX + nParent->width/2 && nParent->pointArray[i].y <= nParent->posY + nParent->height/2)
                    {   
                        // Add the point to the child node's point array
                        n->pointArray[j].x = nParent ->pointArray[i].x;
                        n->pointArray[j].y = nParent ->pointArray[i].y;
                        j++;
                    }
                }
                break;
    
            case 3: // SE
                for(i = 0; i < numParentPoints-1; i++)
                {
                    // Check all parent points and determine if it is in the bottom right quadrant
                    if(nParent->pointArray[i].x<=500 && nParent->pointArray[i].x > nParent->posX +  nParent->width/2 && nParent->pointArray[i].y > nParent->posY && nParent->pointArray[i].x <= nParent->posX + nParent->width && nParent->pointArray[i].y <= nParent->posY + nParent->height/2)
                    {
                        // Add the point to the child node's point array
                        n->pointArray[j].x = nParent ->pointArray[i].x;
                        n->pointArray[j].y = nParent ->pointArray[i].y;
                        j++;
                    }
                }
                break;
    
        }
        return n;
    }
    

    【讨论】:

    • 这是一个很好的答案,我已经赞成,但我注意到第一个示例不是 R-Tree。维基百科显示了一些非常不同的东西。也许它可能是grid file? (我不知道它是什么,我还在努力寻找......)
    • @SteliosAdamantidis 你完全正确,看起来网格索引在这里是正确的术语。感谢您的支持和评论。我将为未来的求职者编辑它?
    【解决方案3】:

    我认为在这种情况下,kd-tree 会比四叉树做得更好,因为当使用四叉树时,为了找到最近的邻居,最近的对象可能会放在节点之间划分的另一侧.另一方面,Kd-trees 允许实现非常有效的最近邻搜索,尽管插入和删除会更加困难,同时保持平衡树。

    【讨论】:

      猜你喜欢
      • 2012-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-11
      • 2013-05-12
      • 2014-06-14
      • 2015-05-09
      • 1970-01-01
      相关资源
      最近更新 更多