四叉树和Kd树

2024-04-17

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

  • Kdtree 和四叉树中哪种算法最适合从纬度和经度集合中找出邻居位置?
  • 一种相对于另一种有什么优势?
  • 我们如何在 C# 中将这些实现到算法中以实现上述目的?

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

什么是网格索引?

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

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

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

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

第一个过滤器创建一组候选点,并防止依次测试我们研究区域中要检查的所有点。

第二个过滤器是精确检查,使用直角坐标来测试候选人。

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

例如,当瓷砖太大时,假设您有一块与学习区域大小相同的瓷砖,这样就只剩下一块瓷砖了!所以第一个过滤器实际上没什么用,整个处理负载将由第二个过滤器承担。在这种情况下,第一个过滤器很快,第二个过滤器非常慢。

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

确定瓦片尺寸非常重要,直接影响性能,但是如果无法确定最佳瓦片尺寸怎么办?如果您所在的区域既有空闲子区域又有密集子区域怎么办? 现在是时候使用其他空间索引机制了,比如 R 树、KD 树或四叉树!

什么是四叉树?

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

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

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

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

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

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

  1. 四叉树创建了很多空的子图块,因为即使我们图块的整个数据可以容纳四分之一,每个图块也必须分为四个部分,所以其余的子图块被认为是多余的。(取一个看上面的四叉树图片)
  2. 四叉树有更容易的索引,并且可以更容易地实现。使用图块 ID 访问图块不需要递归函数。
  3. 在二维 Kd-Tree 中,每个节点只有两个子节点或根本没有子节点,因此搜索 KD-Tree 是二分查找在自然界。
  4. 更新四叉树比更新平衡 KD 树容易得多。

根据上面的描述,我建议从四叉树开始

这是一个四叉树的示例代码,旨在创建 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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

四叉树和Kd树 的相关文章

随机推荐

  • 如何在java中使用twitter4j发布推文?

    我可以使用 twitter4j 登录 twitter 并获取有关登录用户的信息 但我无法从我的应用程序发布推文 我正在使用 Java Web 应用程序来发布推文 请参阅下面我使用的代码 String ACCESS TOKEN ttttttt
  • boost read_until 不会在分隔符处停止

    我使用 boost read until 函数来促进通过套接字接收和解析 HTTP 消息 所以我是什么trying要做的就是从套接字中 read until 直到 r n 我认为应该给我一行 HTTP 标头 每个 HTTP 标头行以 r n
  • 如何在 Windows 计算机上编译适用于 Linux 的 .NET Core 应用程序

    我正在 Windows 10 计算机上开发一个 NET Core 应用程序 使用 Visual Studio 2015 update 3 Microsoft NET Core 1 0 1 VS 2015 Tooling Preview 2
  • 是否可以在具有 $elemMatch 投影的同一集合上使用查询投影?

    我知道您可以使用以下方法限制子集合数组中的项目 elemMatch http docs mongodb org manual reference operator projection elemMatch 作为投影 当这样使用它时 它会返回
  • 分割视图控制器必须是根视图控制器

    每当我尝试以模态方式呈现 UISplitViewController 时 应用程序就会崩溃 因此它必须始终是根视图控制器 谁能证实这一点吗 来自Apple iPad 编程指南 http developer apple com iphone
  • ROracle dbWriteTable 为 R DATE 列创建 Oracle TIMESTAMP 列

    我正在尝试在 Windows 7 64 位上使用 64 位 R3 0 0 中的 ROracle 包 1 1 10 将一些数据上传到我的 Oracle 11g 数据库 ROracle 帮助dbWriteTable states 日期和 POS
  • 在 Pandas 图中添加图例

    我正在使用 Pandas Plot 绘制密度图 但我无法为每个图表添加适当的图例 我的代码和结果如下 for i in tickers df pd DataFrame dic 2 i mean np average dic 2 i std
  • Telegram (Telesharp) - 海量请求(讨论防洪限制)

    我在用着TLSharp https github com sochix TLSharp用于连接到 Telegram 服务 我想搜索 400 000 个频道 请致电服务人员搜索用户异步40万次 我每 15 秒调用一次此服务 但我得到了 1 天
  • Python 读取命名为 PIPE

    我在 linux 中有一个命名管道 我想从 python 中读取它 问题在于 python 进程连续 消耗 一个核心 100 我的代码如下 FIFO var run mypipe os mkfifo FIFO with open FIFO
  • 如何解决 Heroku 上部署的 python 应用程序上的“500 内部服务器错误”?

    基本上 我有一个即将到来的学校项目 任何计算机科学主题 我决定构建一个元数据查看器 我不是程序员或编码员 我的编码课程今年开始 这个项目只是为了介绍 我可以使用在线资源 所以 我刚刚看到了这个GitHub 存储库 https github
  • 如何 git 推送 reflog?

    有没有办法将引用日志推送到远程 这似乎是一件非常有用的事情 但我不知道如何做到这一点 我正在设想类似的事情git push include reflogs 最后 我希望遥控器在推送时有一份引用日志的逐字副本 我尝试使用 mirror 但是
  • 如何使用 Qt 使用鼠标更改网格布局单元格的大小?

    我使用网格布局 水平和垂直 我喜欢这样一个事实 调整窗口大小时会填充整个窗口内容 但这个扩展管理不善 我经常想只改变网格布局中一列的大小而不改变窗口的大小 例如在 Windows 资源管理器中 有两列 左侧的目录列表及其从左侧到右侧的内容
  • 将 pandas groupby 结果合并回 DataFrame

    我有一个看起来像这样的数据框 idn value 0 ID1 25 1 ID1 30 2 ID2 30 3 ID2 50 我想在此框架中添加另一列 即按 idn 分组的最大 值 我想要一个看起来像这样的结果 idn value max va
  • SD卡传输(存储空间不足)

    我试图让我的应用程序能够移动到 SD 卡 到目前为止 我已将属性 android installLocation auto 添加到我的清单文件中 当我尝试在手机上将应用程序的存储选项从内部移动到外部 75MB 时 可以选择移动它 但在完成
  • 通过 HTTPS 的 Ajax GET 请求

    我怎样才能发送ajaxGET请求结束HTTPS get抛出这个 XMLHttpRequest cannot load https Origin null is not allowed by Access Control Allow Orig
  • 让人们在电影院就座

    这是基于我读到的一篇关于大型软件公司提出的谜题和面试问题的文章 但它有一个转折 一般问题 有一种算法可以让人们在电影院就座 让他们直接坐在朋友旁边 而不是敌人旁边 技术问题 给定一个 N M 网格 用 N M 1 项填充网格 每个项目都有一
  • 作业计划程序未在 Android N 上运行

    作业计划程序在 Android Marshmallow 和 Lollipop 设备上按预期工作 但在 Nexus 5x Android N 预览版 上未运行 安排作业的代码 ComponentName componentName new C
  • Chrome 堆快照——分离节点没有颜色

    我正在跟进本教程 https developers google com web tools chrome devtools memory problems 在 使用堆快照发现分离的 DOM 树内存泄漏 下 当我搜索分离节点时 我看到一堆
  • 如何在 Spring 中将对象添加到应用程序范围

    我们可以使用设置请求属性Model or ModelAndViewSpring 中的对象 我们可以用 SessionAttributes将属性保留在会话范围内 那么我怎样才能将属性放入applicationSpring中的作用域 sprin
  • 四叉树和Kd树

    我有一组不同位置的纬度和经度 也知道我当前位置的纬度和经度 我必须找出距离当前位置最近的地方 Kdtree 和四叉树中哪种算法最适合从纬度和经度集合中找出邻居位置 一种相对于另一种有什么优势 我们如何在 C 中将这些实现到算法中以实现上述目