四棵树和Kd树

时间:2017-03-18 11:04:04

标签: c# geolocation nearest-neighbor kdtree quadtree

我有各种位置的纬度和经度,也知道我当前位置的纬度和经度。我必须从当前位置找出最近的地方。 哪种算法最好来自Kdtree和四叉树来找出纬度和经度集中的邻居位置? 一个优于其他的优势是什么? 你能否对此有所了解? 另外,为了上述目的,我们如何在c#中实现这些算法呢? 提前谢谢你的答案。

3 个答案:

答案 0 :(得分:6)

比较空间索引技术我想在我们的比较研究中引入第三个,称为R-tree。为了理解Quad-Tree,我想首先进入R-tree。

什么是R-Tree?

R-Tree是一种基于网格的空间索引方法,其中研究区域被划分为具有固定尺寸的棋盘,如棋盘。

使用R-Tree,图块中的每个点都标有该图块编号,因此Index表可以为每个点提供一个标记,显示我们的编号所在的图块。

RTree Tiles

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

  1. 找到矩形重叠的切片,以及切片中的点(第一个过滤器)
  2. 在上面的步骤中找到实际位于矩形中的候选点。这需要使用点和矩形坐标精确完成。(第二个过滤器)
  3. 第一个过滤器会创建一组候选项,并阻止测试我们研究区域中的所有点一个接一个地进行检查。

    第二个过滤器是精确检查并使用矩形坐标来测试候选者。

    R Tree, Rectangle Query

    现在,看一下上面图片中的瓷砖,如果瓷砖很大或很小,会发生什么?

    当瓷砖太大时,例如假设您的研磨区域的瓷砖大小相同,这只会产生一个瓷砖!所以第一个过滤器实际上是无用的,整个处理负荷将是第二个过滤器的负担。在这种情况下,第一个过滤器很快,第二个过滤器很慢。

    现在想象一下瓷砖非常小,在这种情况下,第一个过滤器非常慢,实际上它会自动生成答案,第二个过滤器很快。

    确定切片尺寸非常重要并直接影响性能但是如果无法确定最佳切片尺寸会怎样?如果你所在地区有备用和密集的子区域怎么办? 现在是时候使用Quad-Tree了!

    什么是Quad-Tree?

    Quad Tree方法以覆盖整个研究区域的大块开始,并将其划分为两条水平线和垂直线以具有四个相等的区域,这些区域是新的区块,然后检查每个区块以查看它是否具有多于一个定义的阈值,其中的点。在这种情况下,瓷砖将再次使用水平和垂直分割线分成四个相等的部分。这个过程一直持续到没有瓦片的点数大于阈值,这是一个递归算法。

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

    Quad-Tree

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

    下图显示了一个平衡的KD树,每条分界线都是一个中位数,它将一个区域划分为两个子区域,点数大致相等。

    2D-Tree

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

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

    1. Quad-Tree创建了许多空子图块,因为即使我们的图块的整个数据可以适合四分之一,每个图块也必须分成四个部分,因此其余的子图块被认为是多余的。 (看看上面的四树图片)
    2. Quad-Tree具有更容易的索引,可以更容易实现。访问具有Tile ID的磁贴不需要递归函数。
    3. 在二维Kd-Tree中,每个节点只有两个子节点或根本没有子节点,因此搜索KD-Tree本质上是二进制搜索
    4. 更新Quad-Tree比更新平衡KD树要容易得多。
    5. 根据以上描述,我建议从Quad-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;
      }
      

答案 1 :(得分:1)

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

答案 2 :(得分:0)

有几个逻辑错误:

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