【问题标题】:Find nearest points in a vector在向量中查找最近的点
【发布时间】:2009-01-22 15:07:50
【问题描述】:

给定一个带有多个值的排序向量,如下例所示:

std::vector<double> f;
f.pushback(10);
f.pushback(100);
f.pushback(1000);
f.pushback(10000);

我正在寻找最优雅的方法来检索任何双 d 紧邻它的两个值。例如,给定值“45”,我希望它返回“10”和“100”。

我正在查看 lower_bound 和 upper_bound,但它们并没有按照我的意愿行事。你能帮忙吗?

编辑:我决定发布我自己的分析器,因为它在某种程度上是我在这个线程中得到的所有有用答案的组合。我投票选出了我认为最有帮助的答案。

谢谢大家,

戴夫

【问题讨论】:

  • 在某些极端情况下您的问题不清楚。如果您在向量 {44, 45, 46} 上使用值“45”,您会期望什么? {44, 45, 45, 46} 呢?
  • 你说得对,我应该澄清一下。对于初学者来说,向量中的值是唯一的。其次,我实际上是在寻找以下两个值:一个是小于给定值的最大值,另一个是大于或等于的最小值。
  • 哦,我所说的优雅并不是指最高效的。我的意思是优雅,就像使用 STL 编写可读代码一样(如果有这样的话)。

标签: c++ stl


【解决方案1】:

您可以使用 equal_range() 在一次调用中获取两个值(如果它们存在)。它返回一个 std::pair 迭代器,第一个是第一个位置,第二个是最后一个位置,您可以在其中插入传递的值而不会违反排序。为了严格满足您的标准,您必须先递减迭代器,然后验证它不等于向量的 begin()。

【讨论】:

    【解决方案2】:

    您可以使用 STL 的 lower_bound 在几行代码中获得您想要的。 lower_bound 在后台使用二进制搜索,因此您的运行时间为 O(log n)。

    double val = 45;
    double lower, upper;
    std::vector<double>::iterator it;
    it = lower_bound(f.begin(), f.end(), val);
    if (it == f.begin()) upper = *it; // no smaller value  than val in vector
    else if (it == f.end()) lower = *(it-1); // no bigger value than val in vector
    else {
        lower = *(it-1);
        upper = *it;
    }
    

    【讨论】:

    • 如果有多个条目具有相同的值,这将失败 - 正如 upper_bound 将失败(但对于比较的另一端)。
    • 不应该。如果您在 {10, 10, 10, 100, 100, 100, ...} 中查找 45,它将正确找到 10 和 100。
    【解决方案3】:

    您可以简单地使用 binary search,它将在 O(log(n)) 中运行。

    这是一个 Lua sn-p(我没有时间用 C++ 做,抱歉),它可以满足您的需求,除了限制条件(无论如何您都没有定义):

    function search(value, list, first, last)
        if not first then first = 1; last = #list end
    
        if last - first < 2 then
            return list[first], list[last]
        end
    
        local median = math.ceil(first + (last - first)/2)
    
        if list[median] > value then
            return search(value, list, first, median)
        else
            return search(value, list, median, last)
        end
    end
    
    local list = {1,10,100,1000}
    
    print(search(arg[1] + 0, list))
    

    取值从命令行搜索:

    $ lua search.lua 10 # didn't know what to do in this case
    10  100
    $ lua search.lua 101
    100 1000
    $ lua search.lua 99
    10  100
    

    【讨论】:

    • 既然你有一个已经排序的列表,那么二分搜索就是要走的路。
    • 是的,但 lower_bound、upper_bound、equal_range 和其他一些是二分搜索的形式。除非我进行一些后处理,否则这仍然无法让我得到我正在寻找的答案。
    • 我在我的答案中添加了一个 sn-p,以向您展示如何做到这一点。我是用 lua 做的,因为我的 c++ 有点生疏(而且我没有太多时间),但它应该很容易翻译。
    【解决方案4】:

    我将发布我自己的分析器,并投票给任何帮助我实现它的人,因为这是我最终将使用的,你们都帮助我得出了这个结论。欢迎评论。

    std::pair<value_type, value_type> GetDivisions(const value_type& from) const
    {
        if (m_divisions.empty())
            throw 0; // Can't help you if we're empty.
    
        std::vector<value_type>::const_iterator it = 
            std::lower_bound(m_divisions.begin(), m_divisions.end(), from);
    
        if (it == m_divisions.end())
            return std::make_pair(m_divisions.back(), m_divisions.back());
        else if (it == m_divisions.begin())
            return std::make_pair(m_divisions.front(), m_divisions.front());
        else
            return std::make_pair(*(it - 1), *(it));
    }
    

    【讨论】:

    • 直到我写完第二个答案后才看到这一点 - 看到你的函数结构非常相似,这并不令人震惊。
    • 一个问题 - 如果“来自”在向量中,这不会崩溃吗?在您最初的问题中,您的值是 10,100,1000,10000。如果'from'是100,你会得到,但你不应该得到吗?
    【解决方案5】:

    如果(在您的情况下)d 小于第一个元素或大于最后一个元素怎么办?以及如何处理负值?顺便说一句:保证你的“d”在你的向量的第一个和最后一个值之间,你可以这样做:

    // Your initializations
    std::vector<double>::const_iterator sit = f.begin();
    double upper, lower; 
    

    这是其余的:

    while ( *sit < d )         // if the element is still less than your d
        ++sit;                 // increase your iterator
    
    upper = *sit;              // here you get the upper value
    lower = *(--sit);          // and here your lower
    

    够优雅吗? :/

    【讨论】:

    • 尽管我仍然会使用 lower_bound,它可以完成您的代码所做的工作,但是使用二进制搜索,您的函数的第二部分让我得出了结论。
    • 好的,很高兴对某人有用:)
    【解决方案6】:

    你可以在你的向量中搜索你的值(如果它在向量中,它会告诉你你的值在哪里),然后返回该位置之前和之后的值。所以搜索 45 会告诉你它应该在 index=1 然后你会返回 0 和 1 (取决于你的搜索实现,你会得到较小值的索引或较大值的索引,但这很容易通过几个边界条件来检查)。这应该能够在 O(log n) 中运行,其中 n 是向量中的元素数。

    【讨论】:

    • 这在我看来就像一个线性搜索,应该是 O(n)。
    • 由于向量与示例中一样,您可以使用二进制搜索 (O(logn))。
    • STL 线性搜索失败(值不存在),lower_bound/upper_bound 将适用于已排序的容器,但时间为 O(log N)。
    【解决方案7】:

    我会写这样的东西,没有测试是否可以编译,但你明白了:

    template <typename Iterator>
    std::pair<Iterator, Iterator> find_best_pair(Iterator first, Iterator last, const typename Iterator::value_type & val)
    {
        std::pair<Iterator, Iterator> result(last, last);
    
        typename Iterator::difference_type size = std::distance(first, last);
    
        if (size == 2)
        {
            // if the container is of size 2, the answer is the two elements 
            result.first = first;
            result.first = first;
            ++result.first;
        }
        else
        {
    
            // must be of at lease size 3
            if (size > 2)
            {
                Iterator second = first;
                ++second;
                Iterator prev_last = last;
                --prev_last;
                Iterator it(std::lower_bound(second, last, val));
    
                if (it != last)
                {
    
                    result.first = it;
                    result.second = it;
    
    
                    if (it != prev_last)
                    {
                        // if this is not the previous last
                        // then the answer is (it, it + 1)
                        ++result.second;
                    }
                    else
                    {
                        // if this the previous last
                        // then the answer is (it - 1, it)
                        --result.first;
                    }
    
                }
    
            }
    
        }
    
        return result;
    
    
    }
    

    【讨论】:

      【解决方案8】:

      我写了这个小函数,它似乎适合你想要的更一般的情况。我还没有完全测试过,但我确实写了一点测试代码(包括在内)。

      #include <algorithm>
      #include <iostream>
      #include <vector>
      
      template <class RandomAccessIt, class Container, class T>
      std::pair<RandomAccessIt, RandomAccessIt> bracket_range(RandomAccessIt begin, RandomAccessIt end, Container& c, T val)
      {
          typename Container::iterator first;
          typename Container::iterator second;
      
          first = std::find(begin, end, val);
          //Find the first value after this by iteration
          second = first;
          if (first == begin){ // Found the first element, so set this to end to indicate no lower values
          first = end;
          }
          else if (first != end && first != begin) --first; //Set this to the first value before the found one, if the value was found
          while (second != end && *second == val) ++second;
          return std::make_pair(first,second);
      }
      
      int main(int argc, _TCHAR* argv[])
      {
          std::vector<int> values;
          std::pair<std::vector<int>::iterator, std::vector<int>::iterator> vals;
      
          for (int i = 1; i < 9; ++i) values.push_back(i);
      
          for (int i = 0; i < 10; ++i){
              vals = bracket_range(values.begin(), values.end(),values, i);
              if (vals.first == values.end() && vals.second == values.end()){ // Not found at all
                  std::cout << i << " is not in the container." << std::endl;
              }
              else if (vals.first == values.end()){ // No value lower
                  std::cout << i << ": " << "None Lower," << *(vals.second) << std::endl;
              }
              else if (vals.second == values.end()) { // No value higher
                  std::cout << i << ": " << *(vals.first) << ", None Higher" << std::endl;
              }
              else{
              std::cout << i << ": " << *(vals.first) << "," << *(vals.second) << std::endl;
              }
          }
          return 0;
      }
      

      【讨论】:

        【解决方案9】:

        根据 tunnuz 发布的代码,这里有一些关于边界检查的改进:

        template<typename T>
        void find_enclosing_values(const std::vector<T> &vec, const T &value, T &lower, T &upper, const T &invalid_value)
        {
            std::vector<T>::const_iterator it = vec.begin();
        
            while (it != vec.end() && *it < value)
                ++it;
        
            if(it != vec.end())
                upper = *it;
            else
                upper = invalid_value;
        
            if(it == vec.begin())
                lower = invalid_value;
            else
                lower = *(--it);
        
        }
        

        使用示例:

        std::vector<int> v;
        
        v.push_back(3);
        v.push_back(7);
        v.push_back(10);
        
        int lower, upper;
        
        find_enclosing_values(v, 4, lower, upper, -1);
        
        std::cout<<"lower "<<lower<<" upper "<<upper<<std::endl;
        

        【讨论】:

        • 很抱歉,我已经有一段时间没有学过 C++了,而且我什至不再从事那项工作了,所以我无法验证这是否正确。跨度>
        【解决方案10】:

        如果您有能力使用其他数据结构(不是向量),我建议您使用B-tree。如果您的数据不变,我相信您可以在恒定时间内(最坏的对数时间)检索结果。

        【讨论】:

          猜你喜欢
          • 2019-06-01
          • 1970-01-01
          • 2015-01-10
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 2011-02-22
          • 1970-01-01
          相关资源
          最近更新 更多