在列表中获得大于x的第一个值的有效方法?

时间:2011-02-14 09:28:52

标签: c++ algorithm floating-point find sortedlist

我有两个排序的数组HaystackNeedles。我需要迭代Needles,每次在Haystack中找到值大于Needle的第一个点,以便执行下一步。

例如:

double [] dHaystack = { 1.2, 2.6, 7.0, 9.3, 19.4 }
double [] dNeedles  = { 1.4, 6.4, 6.5, 7.0, 10.3 }

//  expected indices     0    1    1    2    3    

所以我应该得到的指数是第一个等于或小于针值的指数。

显而易见的方法是从大海捞针的每个针头开始迭代,或者从最后找到的索引向前迭代(因为Needles也被排序)。

但是我脑子里的一部分是喊“一分为二”!这里的二分法实际上会更快,因为编译器会发现优化比简单的块读取和迭代更难吗?它需要一个令人难以置信的长干草堆值得吗?

4 个答案:

答案 0 :(得分:2)

您需要考虑该方案,

n * lg(m)< N + M

其中n是Needle的大小,m是Haystack的大小。

因此,这一切都取决于n和m值的各种组合。

答案 1 :(得分:1)

std :: upper_bound将为第一个元素提供迭代器,如果它们都不适用,则为集合的“更大”或“结束”

upper_bound接收开始和结束的迭代器,结束是集合结束的一个。如果您正在迭代不断增加的搜索值列表,那么您当然不需要遍历整个集合,但您的“开始”可以进一步向右移动。

当然,只有5个元素的干草堆,你使用什么样的搜索算法并不重要,但是如果它变得非常大,使用线性搜索可能会非常慢,特别是如果只有很少的针。< / p>

这种情况下两种尺寸都很重要。例如,如果您的搜索空间N很大但搜索的项目数(M)很小,则O(M log N)实际上要小得多。 (例如,M = 20,N = 16K,则log N = 15,M log N为300),与O(M + N)相比,在这种情况下为16K。如果M的大小与N大致相同,则O(M log N)实际上比O(N)差很多。

因此,根据您的馆藏大小,您可以选择使用哪种算法。

答案 2 :(得分:1)

  

显而易见的方法是从最后找到的索引开始迭代...(因为Needles也是排序的)。

  

但是我脑子里的一部分是喊“一分为二”!这里的二分法实际上会更快,因为编译器会发现优化比简单的块读取和迭代更难吗?它需要一个令人难以置信的长干草堆值得吗?

我不认为编译器优化是一个问题(它只是删除了不必要的工作),而不是实际固有的必要工作量。如果两组的大小相似,那么我会坚持使用明显的方法。如果干草堆比针组大得多,则二分或甚至插值可能会产生稍好的性能。除非这对你的应用程序至关重要,否则你不太可能注意到这些差异,如果它是你应该进行基准测试,特别是因为你可以使用std::set和上限或下限快速获得一个有效的实现(我永远不会记得我需要的 - 不要经常使用),如果您的图书馆支持,可以使用最后一个位置作为起始位置的提示。

答案 3 :(得分:1)

使用std :: upper_bound,它是随机访问迭代器的O(log n),并在最短和最简单的代码中提供您所需的内容。

在您担心每分钟性能之前,请测试您当前的代码(可能还有测试备选方案)instead of making assumptions。特别注意,您可以从每次迭代的最后找到的索引开始搜索(第一个参数为upper_bound)。

// Available in Boost, C++0x, and many other places.  Implementation copied
// here for the sake of the example.
template<class T, int N>
T* end(T (&a)[N]) {
  return a + N;
}

void example() {
  double haystack[] = {1.2, 2.6, 7.0, 9.3, 19.4};
  double needles[] = {1.4, 6.4, 6.5, 7.0, 10.3};
  double *begin = haystack;
  for (double *n = needles; n != end(needles); ++n) {
    double *found = std::upper_bound(begin, end(haystack), *n);
    if (found == end(haystack)) break;
    std::cout << *n << " at index " << (found - haystack) << '\n';
    begin = found;
  }
}