【计算方法】正交区域查询---KD-Tree概念

news2024/12/23 5:43:21

   

一、说明

        kd 树是一种二叉树数据结构,可以用来进行高效的 kNN 计算。kd 树算法偏于复杂,本篇将先介绍以二叉树的形式来记录和索引空间的思路,以便读者更轻松地理解 kd 树。

二、正交区域查找

2.1 定义

        对于k维空间的张量数据表格,如果需要找出超立方体的区域内部数据的查找方法。之所以称之为正交区域查找,是因为在k维张量中,属性维度的空间相互无关。
        而许多信息的查询是可以转化为正交区域查找的,例如问一堆员工中,年龄在[a,b],工资在[l,r]中的有几个,家庭人数为【n,m】。这个有很多做法,什么树套树之类的。而一种思路是,把员工的年龄x、工资y、家庭成员z,映射到三维平面上的点(x,y,z)上,这样就可以进行正交区域查找了,即查找一个矩形中点的个数。


        对于更高维查询,我们需要一个数据结构,该结构可以在任何维数下使用 。* 注意:如果用树嵌套查询不足以构成各维度对等模型,因此,二叉树的迭代查询是不可取的。

 2.2 引进KD树

        先解释一下名字,K是维数,D是Dimension,即维。“树”表明他是树的结构。基本地,KD树中一个节点储存了:

  • K维空间域,(例如三维中的一个长方体),
  • 一个K维点的坐标
  • 两个儿子下标

        在平衡树中,我们知道:可以维护以每个节点为根的子树权值的min和max。
        如法泡制,K维空间域与此很类似,维护的是子树点的坐标范围。

const int K=3;
struct KD_Tree
{
    int d[K],son[2];
    int x[2],y[2],z[2]     ;//Range[K][2];
} tr[N];

        如上代码,P为节点储存的原图的点坐标,son为儿子,第二行储存了K维空间域。

2.3 构造Kd树

基本思想:

  • KD树是一颗平衡二叉树,其中每个非叶节点,可以想象一个超平面,用来分割其储存的空间域,其中超平面垂直于坐标轴。
  • 树尽量平衡,超平面划分的两个空间内的点尽量一样多。
  • 为了有扩展性,树的每一层的超平面垂直的坐标轴,要轮流来取。即第一层垂直x轴,第二层垂直y轴,第三层垂直z轴····

        垂直某个轴,意味着以这个轴的坐标为关键字来操作。
        例如这次要垂直x轴,我们取当前点集的x坐标的中位数,然后把它作为切分点,切分点作为父节点,即KD树中新节点储存的点;切开的两边的点分别属于左右子树的点集。

2.4 二维的例子说明原理

1)有二维点如下图:

 2)建立2d的平衡树x轴节点

        在x轴上找二分线l1

3)建立2d的平衡树y轴节点

深度优先算法:

  • 在x的l1线的左侧找到y轴上的二分线l2

  • 在l1和l2包含区域找x的二分线l4

 

 

 

 

完成图:

 

三、三维度示例研究

3.1 假如下面例子

这是一个例子:血型、血小板数、血压三个指标。就按照x,y,z交替选中进行二叉树构建。

3.2 构建示例代码(python)

下面给出构造代码

class KDTree(object):
    
    """
    A super short KD-Tree for points...
    so concise that you can copypasta into your homework 
    without arousing suspicion.
    This implementation only supports Euclidean distance. 
    The points can be any array-like type, e.g: 
        lists, tuples, numpy arrays.
    Usage:
    1. Make the KD-Tree:
        `kd_tree = KDTree(points, dim)`
    2. You can then use `get_knn` for k nearest neighbors or 
       `get_nearest` for the nearest neighbor
    points are be a list of points: [[0, 1, 2], [12.3, 4.5, 2.3], ...]
    """
    def __init__(self, points, dim, dist_sq_func=None):
        """Makes the KD-Tree for fast lookup.
        Parameters
        ----------
        points : list<point>
            A list of points.
        dim : int 
            The dimension of the points. 
        dist_sq_func : function(point, point), optional
            A function that returns the squared Euclidean distance
            between the two points. 
            If omitted, it uses the default implementation.
        """

        if dist_sq_func is None:
            dist_sq_func = lambda a, b: sum((x - b[i]) ** 2 
                for i, x in enumerate(a))
                
        def make(points, i=0):
            if len(points) > 1:
                points.sort(key=lambda x: x[i])
                i = (i + 1) % dim
                m = len(points) >> 1
                return [make(points[:m], i), make(points[m + 1:], i), 
                    points[m]]
            if len(points) == 1:
                return [None, None, points[0]]
        
        def add_point(node, point, i=0):
            if node is not None:
                dx = node[2][i] - point[i]
                for j, c in ((0, dx >= 0), (1, dx < 0)):
                    if c and node[j] is None:
                        node[j] = [None, None, point]
                    elif c:
                        add_point(node[j], point, (i + 1) % dim)

        import heapq
        def get_knn(node, point, k, return_dist_sq, heap, i=0, tiebreaker=1):
            if node is not None:
                dist_sq = dist_sq_func(point, node[2])
                dx = node[2][i] - point[i]
                if len(heap) < k:
                    heapq.heappush(heap, (-dist_sq, tiebreaker, node[2]))
                elif dist_sq < -heap[0][0]:
                    heapq.heappushpop(heap, (-dist_sq, tiebreaker, node[2]))
                i = (i + 1) % dim
                # Goes into the left branch, then the right branch if needed
                for b in (dx < 0, dx >= 0)[:1 + (dx * dx < -heap[0][0])]:
                    get_knn(node[b], point, k, return_dist_sq, 
                        heap, i, (tiebreaker << 1) | b)
            if tiebreaker == 1:
                return [(-h[0], h[2]) if return_dist_sq else h[2] 
                    for h in sorted(heap)][::-1]

        def walk(node):
            if node is not None:
                for j in 0, 1:
                    for x in walk(node[j]):
                        yield x
                yield node[2]

        self._add_point = add_point
        self._get_knn = get_knn 
        self._root = make(points)
        self._walk = walk

    def __iter__(self):
        return self._walk(self._root)
        
    def add_point(self, point):
        """Adds a point to the kd-tree.
        
        Parameters
        ----------
        point : array-like
            The point.
        """
        if self._root is None:
            self._root = [None, None, point]
        else:
            self._add_point(self._root, point)

    def get_knn(self, point, k, return_dist_sq=True):
        """Returns k nearest neighbors.
        Parameters
        ----------
        point : array-like
            The point.
        k: int 
            The number of nearest neighbors.
        return_dist_sq : boolean
            Whether to return the squared Euclidean distances.
        Returns
        -------
        list<array-like>
            The nearest neighbors. 
            If `return_dist_sq` is true, the return will be:
                [(dist_sq, point), ...]
            else:
                [point, ...]
        """
        return self._get_knn(self._root, point, k, return_dist_sq, [])

    def get_nearest(self, point, return_dist_sq=True):
        """Returns the nearest neighbor.
        Parameters
        ----------
        point : array-like
            The point.
        return_dist_sq : boolean
            Whether to return the squared Euclidean distance.
        Returns
        -------
        array-like
            The nearest neighbor. 
            If the tree is empty, returns `None`.
            If `return_dist_sq` is true, the return will be:
                (dist_sq, point)
            else:
                point
        """
        l = self._get_knn(self._root, point, 1, return_dist_sq, [])
        return l[0] if len(l) else None

下面给出测试代码 

import unittest
import random
import cProfile
from kd_tree import *

class KDTreeUnitTest(unittest.TestCase):

    def test_all(self):

        dim = 3

        def dist_sq_func(a, b):
            return sum((x - b[i]) ** 2 for i, x in enumerate(a))

        def get_knn_naive(points, point, k, return_dist_sq=True):
            neighbors = []
            for i, pp in enumerate(points):
                dist_sq = dist_sq_func(point, pp)
                neighbors.append((dist_sq, pp))
            neighbors = sorted(neighbors)[:k]
            return neighbors if return_dist_sq else [n[1] for n in neighbors]

        def get_nearest_naive(points, point, return_dist_sq=True):
            nearest = min(points, key=lambda p:dist_sq_func(p, point))
            if return_dist_sq:
                return (dist_sq_func(nearest, point), nearest) 
            return nearest

        def rand_point(dim):
            return [random.uniform(-1, 1) for d in range(dim)]

        points = [rand_point(dim) for x in range(10000)]
        additional_points = [rand_point(dim) for x in range(100)]
        query_points = [rand_point(dim) for x in range(100)]

        kd_tree_results = []
        naive_results = []
        
        global test_and_bench_kd_tree
        global test_and_bench_naive

        def test_and_bench_kd_tree():
            global kd_tree
            kd_tree = KDTree(points, dim)
            for point in additional_points:
                kd_tree.add_point(point)
            kd_tree_results.append(tuple(kd_tree.get_knn([0] * dim, 8)))
            for t in query_points:
                kd_tree_results.append(tuple(kd_tree.get_knn(t, 8)))
            for t in query_points:
                kd_tree_results.append(tuple(kd_tree.get_nearest(t)))

        def test_and_bench_naive():
            all_points = points + additional_points
            naive_results.append(tuple(get_knn_naive(all_points, [0] * dim, 8)))
            for t in query_points:
                naive_results.append(tuple(get_knn_naive(all_points, t, 8)))
            for t in query_points:
                naive_results.append(tuple(get_nearest_naive(all_points, t)))

        print("Running KDTree...")
        cProfile.run("test_and_bench_kd_tree()")
        
        print("Running naive version...")
        cProfile.run("test_and_bench_naive()")

        print("Query results same as naive version?: {}"
            .format(kd_tree_results == naive_results))
        
        self.assertEqual(kd_tree_results, naive_results, 
            "Query results mismatch")
        
        self.assertEqual(len(list(kd_tree)), len(points) + len(additional_points), 
            "Number of points from iterator mismatch")

if __name__ == '__main__':
    unittest.main()

参考文章:

GitHub - Vectorized/Python-KD-Tree: A simple and fast KD-tree for points in Python for kNN or nearest points. (damm short at just ~60 lines) No libraries needed.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/433107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity RenderStreaming 云渲染3.1.0-exp.6 食用手册

Unity云渲染 &#x1f957;资源&#x1f364;兼容性&#x1f367;手机端连接&#x1f969;安装方法&#x1f35b;IP端口设置&#x1f371;官方案例尝鲜&#x1f332;导入案例&#x1f332;添加场景&#x1f332;启动WebApp&#x1f332;打开Menu场景&#x1f332;连接参数设置&…

300左右蓝牙耳机推荐哪个好?300元左右最好的蓝牙耳机

蓝牙耳机如今在我们的生活中太普遍了&#xff0c;记得疫情刚开始天天要戴口罩&#xff0c;口罩的绳子和耳机线相缠十分的不方便&#xff0c;所以更多的人选择蓝牙耳机&#xff0c;下面整理了几款300元左右的蓝牙耳机品牌。 一、南卡小音舱Lite2蓝牙耳机 售价&#xff08;&…

4-数据结构

数据结构&#xff08;data structure&#xff09; 1. 简介 数据结构是在计算机中组织与存储数据的方式 如果想要表示“一排数字”&#xff0c;自然想到使用「数组」数据结构 数组的存储方式可以表示数字的相邻关系、顺序关系&#xff0c;但至于其中存储的是整数int&#xff0c…

【数据结构:线性表】顺序表

⚡线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直…

网络请求实战-RESTFUL约定和Postman工具

RESTFUL协议 表现层状态转化&#xff08;Representational state transfer&#xff09; 资源、表示和转换 资源&#xff08;Resource&#xff09; 服务端的一个资源 拥有URL 表示&#xff08;Representation&#xff09; 服务端的资源在客户端的表示 客户端拥有操作服务…

three.js之scene

THREE.Scene对象有时被称为场景图&#xff0c;可以用来保存所有图形场景的必要信息。在Three.js中&#xff0c;这意味着THREE.Scene保存所有对象、光源和渲染所需的其他对象。 本节主要是构建一个基本场景&#xff0c;然后可以通过gui添加&#xff0c;删除场景里的对象等。 效果…

lua变量、数据类型、if判断条件和数据结构table以及【lua 函数】

一、lua变量【 全局变量和局部变量和表中的域】 Lua 变量有三种类型&#xff1a;全局变量和局部变量和表中的域。 ▪ 全局变量&#xff1a;默认情况下&#xff0c;Lua中所有的变量都是全局变量。 ▪ 局部变量&#xff1a;使用local 显式声明在函数内的变量&#xff0c;以及函数…

Golang每日一练(leetDay0040)

目录 118. 杨辉三角 Pascals Triangle &#x1f31f; 119. 杨辉三角 II Pascals Triangle II &#x1f31f; 120. 三角形最小路径和 Triangle &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/…

港联证券策略:A股市场短期预计将保持区间震荡

港联证券以为&#xff0c;跟着4月30日的接近&#xff0c;一切板块的年报以及一季度报将进入密布发表期。从历史数据来看&#xff0c;4月中旬今后指数震动调整的概率较大&#xff0c;在操作上需注意操控全体仓位。从板块方面看&#xff0c;近期为数字经济和中特估双主线驱动的行…

Adobe lr中文版下载官方版 专业摄影师的必备软件 附各个平台

说到修图软件&#xff0c;大家第一时间一定都能联想到Adobe旗下的Photoshop&#xff08;Adobe Photoshop CC&#xff09;和Lightroom&#xff08;Adobe Photoshop Lightroom CC&#xff09;&#xff0c;其中PhotoShop很多设计师都找到了对应的手机版&#xff0c;但有些用户由于…

MacOS 上安装并配置 OpenJDK 图文详细版

1、概述 1.1、什么是 OpenJDK &#xff1f; OpenJDK 是 Java 的一个开源实现&#xff0c;由 Sun Microsystems 于2006年开始发布&#xff0c;是 Java SE 平台&#xff08;Java Standard Edition&#xff09;的参考实现之一。OpenJDK 是由 GPL v2 授权下的自由软件&#xff0c…

Msray-Plus采集工具帮您快速获取数据,让您的市场营销更加精细

随着互联网的不断发展&#xff0c;数据已经成为企业竞争的重要资产之一。市场营销人员需要通过数据来了解客户需求、市场趋势和竞争对手情况&#xff0c;从而制定更加精细的市场营销策略。然而&#xff0c;采集数据并不是一件容易的事情&#xff0c;需要耗费大量的时间和精力。…

Java多线程基础学习(一)

1. 创建线程 1.1 通过构造函数&#xff1a;public Thread(Runnable target, String name){} 或&#xff1a;public Thread(Runnable target){} 示例: Thread thread1 new Thread(new MyThread(), "mythread"); class MyThread extends Thread(){public void …

天阳转债上市价格预测

天阳转债 基本信息 转债名称&#xff1a;天阳转债&#xff0c;评级&#xff1a;AA-&#xff0c;发行规模&#xff1a;9.75亿元。 正股名称&#xff1a;天阳科技&#xff0c;今日收盘价&#xff1a;15.68元&#xff0c;转股价格&#xff1a;14.92元。 当前转股价值 转债面值 / …

RHCE---服务+ansible

一、命令提示符颜色 也可以写入配置文件/etc/profile [rootmaster ~]# vim .bashrc 绿色&#xff1a; PS1\[\033[01;32m\]\u\H:\[\033[01;34m\]\w\$\[\033[00m\] 粉红色&#xff1a; PS1\[\e[1;35m\]\u\h:\[\e[0m\]\[\e[1;33m\]\w\[\e[1;35m\]\[\e[0m\]\[\e[1;34m\]\$ \[\e[0…

[架构之路-168]-《软考-系统分析师》-4-据通信与计算机网络-4/5- 常见网络设备与网络工程

目录 4 . 4 网络互连与常用设备 1 . 网络互连设备 2 . 交换技术 3 . 路由技术 4 . 5 网络工程 4.5.1 网络规划&#xff08;要做什么&#xff1f;打算怎么做&#xff1f; 什么时间做&#xff1f;&#xff09; 1 . 网络需求分析 2 . 可行性研究 3 . 对现有网络的分析与描…

【Dom获取属性操作】JavaScript 全栈体系(十)

Web APIs 第四章 操作元素属性 一、操作元素常用属性 还可以通过 JS 设置/修改标签元素属性&#xff0c;比如通过 src更换 图片最常见的属性比如&#xff1a; href、title、src 等语法&#xff1a; 对象.属性 值 <!DOCTYPE html> <html lang"en">&…

【c#串口通信(4)】串口调试助手的使用

前言: 关于本文中使用到的串口助手工具,订阅专栏后,加入vip群,即可在群文件免费下载 1、打开串口 1.1 选择端口号 在下图的箭头所指处,选择下拉框中你想打开的串口,因为我之前使用虚拟串口工具虚拟了COM1、COM3,所以我可以先选择COM2 1.2 设置好参数 1.2.1 波特率:…

Python数据结构与算法篇(九)--单调栈与单调队列

1 单调栈 1.1 介绍 栈&#xff08;stack&#xff09;是很简单的一种数据结构&#xff0c;先进后出的逻辑顺序&#xff0c;符合某些问题的特点&#xff0c;比如说函数调用栈。 单调栈实际上就是栈&#xff0c;只是利用了一些巧妙的逻辑&#xff0c;使得每次新元素入栈后&#x…

rhcse中配置DNS的正反向解析

实验一 正向解析 服务端ip 192.168.9.30 客户端ip 192.168.9.31 网址 www.openlab.com 安装DNS软件 服务端 [rootlocalhost ~]# yum install bind -y 客户端与服务端相同 编辑DNS主配置文件 修改其中的11和19行 listen-on port 53 { any; }; allow-query { a…