C# 创建KD树的程序

实现了KD平衡树的程序,由于MATLAB实现需要用到引用类型或者采用循环实现(见MATLAB的KDTreeSearcher.m),因此采用C#来实现

using System;
using System.Collections.Generic;
using System.Linq;


namespace KNNSearch
{
    /// 
    /// Description of KNN.
    /// 
    public class KNN
    {
        /// 
        /// 叶子节点点的个数
        /// 
        private int leafnum = 30;
        /// 
        /// 待分类数据
        /// 
        private List
   
   
    
     rawData;
        /// 
    
    
        /// 生成原始数据
        /// 
        private void GeneralRawData()
        {
            if (rawData == null)
            {
                rawData = new List
    
    
     
     ();
            }
            else
            {
                rawData.Clear();
            }
            Random r = new Random();
            for (int i = 0; i < 500; i++)
            {
                rawData.Add(new Point() { X = r.NextDouble(), Y = r.NextDouble(), Z = r.NextDouble() });
            }

        }

        /// 
     
     
        /// 创建KD树
        /// 
        /// 
     
     
        /// 
     
     
        private Node CreateKDTree(List
     
     
      
       data)
        {
            // 创建根节点
            Node root = new Node();
            // 添加当前节点数据
            root.nodeData = data;
            // 如果节点的数据数量小于叶子节点的数量限制,则当前节点为叶子节点
            if (data.Count <= leafnum)
            {
                root.leftNode = null;
                root.rightNode = null;
                root.point = double.NaN;
                root.splitaxis = -1;
                return root;
            }
            // 找到分割轴
            int splitAxis = GetSplitAxis(data);
            // 分割数据
            Tuple
      
      
       
       , List
       
       
         > dataSplit = GetSplitNum(data, splitAxis); root.splitaxis = splitAxis; root.point = dataSplit.Item1; root.leftNode = CreateKDTree(dataSplit.Item2); root.rightNode = CreateKDTree(dataSplit.Item3); return root; } private Tuple 
         
         
           , List 
          
            > GetSplitNum(List 
           
             data, int splitAxis) { // 对数据按照第splitAxis排序 var data0 = splitAxis == 0 ? (data.OrderBy(x => x.X)).ToList() : (splitAxis == 1 ? (data.OrderBy(x => x.Y)).ToList() : (data.OrderBy(x => x.Z)).ToList()); int half = data0.Count / 2; List 
            
              leftdata = new List 
             
               (); List 
              
                rightdata = new List 
               
                 (); for (int i = 0; i < data0.Count; i++) { if (i <= half) { leftdata.Add(data0[i]); } else { rightdata.Add(data0[i]); } } double splitnum = splitAxis == 0 ? data[half].X + data[half + 1].X : (splitAxis == 1 ? data[half].Y + data[half + 1].Y : data[half].Z + data[half + 1].Z); return new Tuple 
                
                  , List 
                 
                   >(splitnum / 2, leftdata, rightdata); } /// 
                   /// 获取分割轴编号 ///  /// 
                   /// 
                   private int GetSplitAxis(List 
                  
                    data) { // 设定数据范围最大的轴作为分割轴(也有其他的方式,如方差,或者轮流的方式) var xData = data.Select(item => item.X); var yData = data.Select(item => item.Y); var zData = data.Select(item => item.Z); List 
                   
                     ranges = new List 
                    
                      (); ranges.Add(xData.Max() - xData.Min()); ranges.Add(yData.Max() - yData.Min()); ranges.Add(zData.Max() - zData.Min()); var sorted = ranges.Select((x, i) => new KeyValuePair 
                     
                       (x, i)).OrderByDescending(x => x.Key).ToList(); return sorted.Select(x => x.Value).ToList()[0]; ; } public KNN() { GeneralRawData(); Node node = CreateKDTree(rawData); } } /// 
                       /// Description of Node. ///  public class Node { /// 
                       /// 切分的阈值点 ///  public double point; /// 
                       /// 左节点 ///  public Node leftNode; /// 
                       /// 右节点 ///  public Node rightNode; /// 
                       /// 节点包含的数据 ///  public List 
                      
                        nodeData; /// 
                        /// 分割轴 ///  public int splitaxis; public Node() { } } public class Point { public double X; public double Y; public double Z; } } 
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KD是一种二叉,用于高效地搜索k维空间中的数据。下面是用C#实现KD的基本步骤: 首先,定义一个节点类,表示KD中的每个节点。节点类中应该包括以下属性: - point:该节点代表的数据点 - axis:该节点的分割维度 - left:该节点的左子 - right:该节点的右子 节点类的定义如下: ```csharp class Node { public double[] point; // 数据点 public int axis; // 分割维度 public Node left; // 左子 public Node right; // 右子 } ``` 接下来,实现一个构建KD的函数。该函数接受一个二维数组作为输入,每个元素代表一个k维数据点。函数应该返回KD的根节点。 构建KD的基本思路是:先选择一个维度作为分割维度,然后按照该维度对数据点进行排序。将排序后的数据点数组的中间点作为当前节点,将数组分为左右两部分,分别递归构建左子和右子。 具体实现如下: ```csharp static Node BuildKDTree(double[][] points) { return BuildKDTree(points, 0); } static Node BuildKDTree(double[][] points, int axis) { if (points.Length == 0) return null; int k = points[0].Length; int median = points.Length / 2; Array.Sort(points, new PointComparer(axis)); Node node = new Node(); node.point = points[median]; node.axis = axis; node.left = BuildKDTree(points.Take(median).ToArray(), (axis + 1) % k); node.right = BuildKDTree(points.Skip(median + 1).ToArray(), (axis + 1) % k); return node; } ``` 其中,PointComparer是一个实现了IComparer接口的类,用于对数据点按照指定维度进行排序。代码如下: ```csharp class PointComparer : IComparer<double[]> { private int axis; public PointComparer(int axis) { this.axis = axis; } public int Compare(double[] x, double[] y) { return x[axis].CompareTo(y[axis]); } } ``` 最后,实现一个搜索KD的函数。该函数接受一个查询点和KD的根节点作为输入,返回离查询点最近的数据点。 搜索KD的基本思路是:从根节点开始,依次向下遍历KD。对于每个节点,计算该节点对应的数据点与查询点的距离,并记录最小距离和对应的数据点。然后根据查询点与当前节点所在的超平面的位置关系,选择左子或右子进行递归搜索。 具体实现如下: ```csharp static double[] NearestNeighborSearch(double[] query, Node root) { double[] best = null; double bestDist = double.PositiveInfinity; NearestNeighborSearch(query, root, ref best, ref bestDist); return best; } static void NearestNeighborSearch(double[] query, Node node, ref double[] best, ref double bestDist) { if (node == null) return; double dist = Distance(query, node.point); if (dist < bestDist) { best = node.point; bestDist = dist; } int k = query.Length; if (query[node.axis] < node.point[node.axis]) { NearestNeighborSearch(query, node.left, ref best, ref bestDist); if (query[node.axis] + bestDist > node.point[node.axis]) NearestNeighborSearch(query, node.right, ref best, ref bestDist); } else { NearestNeighborSearch(query, node.right, ref best, ref bestDist); if (query[node.axis] - bestDist < node.point[node.axis]) NearestNeighborSearch(query, node.left, ref best, ref bestDist); } } static double Distance(double[] p1, double[] p2) { double sum = 0; for (int i = 0; i < p1.Length; i++) sum += (p1[i] - p2[i]) * (p1[i] - p2[i]); return Math.Sqrt(sum); } ``` 至此,就完成了用C#实现KD的整个过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值