【问题标题】:Clear And Efficient 3D Range Tree Implementation清晰高效的 3D 范围树实施
【发布时间】:2012-12-02 00:35:17
【问题描述】:

我正在从事这个项目,我必须在 3d 空间中搜索对象,效率是一个巨大的问题,我认为 Range Tree 非常适合我正在尝试做的事情,间隔树也可以,但我不会从树中删除任何内容,一旦我在 3D 空间中添加每个对象,我只会使用该结构进行搜索。

以下是我将如何使用该结构:

假设我有一个对象数组(我们称之为 queryArr)(约 10,000 个对象)每个对象有 3 个参数(x,y,z)我有另一个非常大的数组(让我们称它为 totalArr) 个对象(> 5,000,000 个对象)。

我在这里要做的是给定 queryArr 的元素,找到最相似的(或 totalArr 中的相同元素)在某些情况下会有一个totalArr 中具有相同参数的对象,但在大多数情况下,不会有具有相同参数的对象。

所以,我将搜索(x+10,y+10,z+10)(x-10,y-10,z-10) 之间的所有值。如果它没有产生任何结果,我将 x,y,z 乘以 2 并重试,直到它产生一些结果。

最简单的方法是简单的搜索方法,它的复杂度为O(N*M) (N = size of queryArr, M = sie of totalArr),但这种方法非常缓慢和愚蠢。

我认为范围树是要走的路,但我自己从未实现过,我不太了解范围树如何在大于 2 的维度上工作。那么有人知道范围树的良好实现吗?我相信如果我有源代码,我就能理解它们的真正工作原理。

顺便说一句,如果您认为这个任务有比 Range Tree 更好的结构,请告诉我,我愿意接受建议。 (我已经考虑过 kd-Trees 和区间树)

谢谢,

【问题讨论】:

    标签: c++ algorithm search data-structures range-tree


    【解决方案1】:

    我刚刚阅读了维基百科的文章。让我们看看我是否可以写一个 n 维范围树。因为在 3 维中任何值得做的事情都值得在 n 中做。

    因此,n 维范围树的基本部分是它可以根据低维范围树递归定义。

    一些属性类可以使用相对通用的值类型。特化 element_properties<T> 设置 n 维值的标量类型,特化 get<i>(T const&) 获取 n 维值的 ith 维。

    #include <memory>
    #include <cstddef>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <sstream>
    
    void Assert(bool test) {
      if (!test)
      {
        std::cout << "Assert failed" << std::endl;
        exit(-1);
      }
    }
    template<typename... Args>
    struct Print {
      static void Do(Args... args) {}
    };
    template<typename Arg, typename... Tail>
    struct Print<Arg, Tail...> {
      static void Do(Arg arg, Tail... args) {
          std::cout << arg;
          Print<Tail...>::Do(args...);
      }
    };
    template<typename... Args>
    void Debug(Args... args) {
        std::cout << "DEBUG:[";
        Print<Args...>::Do(args...);
        std::cout << "]\n";
    }
    
    template<typename T>
    struct element_properties {
      typedef typename T::value_type value_type;
    };
    template<>
    struct element_properties<int> {
      typedef int value_type;
    };
    template<size_t d, typename T>
    typename element_properties<T>::value_type get( T const & t );
    
    template<size_t d>
    typename element_properties<int>::value_type get( int i ) { return i; }
    
    template<size_t d, typename U, typename A>
    typename element_properties<std::vector<U,A>>::value_type get( std::vector<U,A> const& v) {
      return v[d];
    }
    
    template<typename T, size_t dim, typename Order = std::less< typename element_properties<T>::value_type> >
    struct range_tree {
      typedef typename element_properties<T>::value_type value_type;
      struct sorter {
        bool operator()( T const& left, T const& right ) const {
          return Order()( get<dim-1>(left), get<dim-1>(right) );
        }
      };
      struct printer {
        std::string operator()( T const& t ) const {
          std::string retval = "[ ";
          retval += print_elements( t );
          retval += "]";
          return retval;
        }
        std::string print_elements( T const& t ) const {
          std::stringstream ss;
          typedef typename range_tree<T, dim-1, Order>::printer next_printer;
          ss << next_printer().print_elements(t);
          ss << get<dim-1>(t) << " ";
          return ss.str();
        }
      };
      template<typename Iterator>
      range_tree( Iterator begin, Iterator end ) {
        std::sort( begin, end, sorter() );
        root.reset( new tree_node( begin, end ) );
      }
    
      template<size_t n, typename Func>
      void walk(Func f) const {
          if (root) root->walk<n>(f);
      }
      template<size_t n, typename Func>
      void walk(Func f) {
          if (root) root->walk<n>(f);
      }
      struct tree_node {
        std::unique_ptr< range_tree<T, dim-1, Order> > subtree;
        T value;
        template<size_t n, typename Func>
        void walk(Func f) const {
          if (n==dim && !left && !right)
            f(value);
          if (left)
            left->walk<n>(f);
          if (right)
            right->walk<n>(f);
          if (subtree)
            subtree->walk<n>(f);
        }
        template<size_t n, typename Func>
        void walk(Func f) {
          if (n==dim && !left && !right)
            f(value);
          if (left)
            left->walk<n>(f);
          if (right)
            right->walk<n>(f);
          if (subtree)
            subtree->walk<n>(f);
        }
        void find_path( T const& t, std::vector< tree_node const* >& vec ) {
          vec.push_back(this);
          if ( sorter()(t, value) ) {
            if (left)
              left->find_path(t, vec);
          } else if (sorter()(value, t)) {
            if (right)
              right->find_path(t, vec);
          } else {
            // found it!
            return;
          }
        }
        std::vector< tree_node const* > range_search( T const& left, T const& right )
        {
          std::vector<tree_node const*> left_path;
          std::vector<tree_node const*> right_path;
          find_path( left, left_path );
          find_path( right, right_path );
          // erase common path:
          {
            auto it1 = left_path.begin();
            auto it2 = right_path.begin();
            for( ; it1 != left_path.end() && it2 != right_path.end(); ++it1, ++it2) {
              if (*it1 != *it2)
              {
                Debug( "Different: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
                break;
              }
    
              Debug( "Identical: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
            }
            // remove identical prefixes:
            if (it2 == right_path.end() && it2 != right_path.begin())
                --it2;
            if (it1 == left_path.end() && it1 != left_path.begin())
                --it1;
            right_path.erase( right_path.begin(), it2 );
            left_path.erase( left_path.begin(), it1 );
          }
          for (auto it = left_path.begin(); it != left_path.end(); ++it) {
            if (*it && (*it)->right) {
              Debug( "Has right child: ", printer()( (*it)->value ) );
              *it = (*it)->right.get();
              Debug( "It is: ", printer()( (*it)->value ) );
            } else {
              Debug( "Has no right child: ", printer()( (*it)->value ) );
              if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
                Debug( printer()( (*it)->value ), "<", printer()( left ), " so erased" );
                *it = 0;
              }
            }
          }
          for (auto it = right_path.begin(); it != right_path.end(); ++it) {
            if (*it && (*it)->left) {
              Debug( "Has left child: ", printer()( (*it)->value ) );
              *it = (*it)->left.get();
              Debug( "It is: ", printer()( (*it)->value ) );
            } else {
              Debug( "Has no left child: ", printer()( (*it)->value ) );
              if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
                Debug( printer()( right ), "<", printer()( (*it)->value ), " so erased" );
                *it = 0;
              }
            }
          }
          left_path.insert( left_path.end(), right_path.begin(), right_path.end() );
          // remove duds and duplicates:
          auto highwater = std::remove_if( left_path.begin(), left_path.end(), []( tree_node const* n) { return n==0; } );
          std::sort( left_path.begin(), highwater );
          left_path.erase( std::unique( left_path.begin(), highwater ), left_path.end() );
          return left_path;
        }
    
        std::unique_ptr<tree_node> left;
        std::unique_ptr<tree_node> right;
        // rounds down:
        template<typename Iterator>
        static Iterator middle( Iterator begin, Iterator end ) {
          return (end-begin-1)/2 + begin ;
        }
        template<typename Iterator>
        tree_node( Iterator begin, Iterator end ):value(*middle(begin,end)) {
          Debug( "Inserted ", get<dim-1>(value), " at level ", dim );
          Iterator mid = middle(begin,end);
          Assert( begin != end );
          if (begin +1 != end) { // not a leaf
            Debug( "Not a leaf at level ", dim );
            ++mid; // so *mid was the last element in the left sub tree 
            Assert(mid!=begin);
            Assert(mid!=end);
            left.reset( new tree_node( begin, mid ) );
            right.reset( new tree_node( mid, end ) );
          } else {
            Debug( "Leaf at level ", dim );
          }
          if (dim > 0) {
            subtree.reset( new range_tree<T, dim-1, Order>( begin, end ) );
          }
        }
      };
      std::unique_ptr<tree_node> root;
    };
    // makes the code above a tad easier:
    template<typename T, typename Order >
    struct range_tree< T, 0, Order > {
      typedef typename element_properties<T>::value_type value_type;
      struct printer { template<typename Unused>std::string print_elements(Unused const&) {return std::string();} };
      range_tree(...) {};
      struct tree_node {}; // maybe some stub functions in here
      template<size_t n, typename Func>
      void walk(Func f) {}
    };
    
    int main() {
      typedef std::vector<int> vector_type;
      std::vector<vector_type> test;
      test.push_back( vector_type{5,2} );
      test.push_back( vector_type{2,3} );
      range_tree< vector_type, 2 > tree( test.begin(), test.end() );
      std::cout << "Walking dim 2:";
      auto print_node = [](vector_type const& v){ std::cout << "(" << v[0] << "," << v[1] << ")"; };
      tree.walk<2>( print_node );
      std::cout << "\nWalking dim 1:";
      tree.walk<1>( print_node );
      std::cout << "\n";
    
      std::cout << "Range search from {3,3} to {10,10}\n";
      auto nodes = tree.root->range_search( vector_type{3,3}, vector_type{10,10} );
      for (auto it = nodes.begin(); it != nodes.end(); ++it)
      {
        (*it)->walk<2>( print_node );
      }
    }
    

    这非常接近于 n 维范围树。 0 维树自然不包含任何内容。

    现在添加了基本的搜索功能(一次只在一个维度上)。您可以手动将递归执行到较低维度,或者向上执行,以便 range_search 始终返回级别 1 tree_node*s。

    【讨论】:

    • 嗨 Yakk,谢谢你的回答,我正在尝试理解你的代码,但我什至无法编译它:( 我试图用 VC++ 和 MinGW 编译它,但没有骰子。这是用 C++0x 编写的代码?,我很困惑,首先为什么要删除相同的前缀?还有 range_tree(...) {}; 做什么时你叫它吗?如果问的不是太多,你能解释一下你的代码吗?非常感谢你的时间和精力:)
    • ... 只是用来丢弃参数。您可能必须将其替换为带有两个迭代器的无操作构造函数。打印和调试代码为 C++11,无法在 Visual Studio 中编译。删除相同的前缀是查找范围算法的一部分——维基百科对此进行了解释。
    猜你喜欢
    • 2015-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多