【问题标题】:What is the fastest way to return a range of numbers from a sorted array of numbers?从排序的数字数组中返回一系列数字的最快方法是什么?
【发布时间】:2018-05-22 08:09:24
【问题描述】:

对于 C++ 语言,从算法设计的角度来看,处理运行时(在多核处理器中)的最快方法是搜索数组(或拼接或其他更快)内的数字(例如 100 到 1000 之间)为此目的的数据结构)并返回仅限于返回 10 个项目的数字范围?例如golang 中的伪代码:

var listofnums := []uint64 
var numcounter := 1
// splice of [1,2,3,4,5,31,32 .. 932536543] this list has 1 billion numeric items.
// the listofnums are already sorted each time an item is added but we do not know the lower_bound or upper_bound of the item list.
// I know I can use binary search to find listofnums[i] where it is smallest at [i] too... I'm asking for suggestions. 
for i:=uint(0); i < len(listofnums); i++ {
    if listofnums[i] > 100 && listofnums[i] < 1000 {
         if listofnums[i]> 1000 || numcounter == 10 {
             return
         }
         fmt.Println("%d",listofnums[i])
         numcounter++
    }
}

这是最快的方法吗?我在 C++ 中看到了位图结构,但不确定是否可以在这里应用。

我遇到过这个问题,这对于资深程序员来说是完全可以问的,但我不知道为什么它被否决了。 What is the fastest search method for array?

有人可以不要删除这个问题,而是让我改写一下吗?提前致谢。我希望找到从大量数字项中返回一系列数字的最佳方法。

【问题讨论】:

  • 哪种语言?选择一个
  • 我需要测试 Go 的内存需求和使用是否比 C++ 执行此范围返回的最快方法值得实现。所以……这样可以吗?我的意思是,如果 Go 使用太多内存来完成此任务,那么我将使用 C++,反之亦然。最快还必须取决于是否值得实施(考虑的因素不仅仅是纯粹的速度)
  • 这还取决于您对数据的其他了解。例如,如果第二个元素已经与 &gt; 100 匹配,那么您的搜索速度很快。如果我们什么都不知道,二分查找会更好。在 C++ 中,你有 lower_boundupper_bound
  • 首先,每个问题你还是应该问一种语言,否则这个问题太板了。其次,你对“最快”的定义太模糊了。是时间复杂度、运行时间还是开发时间?第三,这种算法与语言无关。第四,语言开销和算法设计是两个问题。
  • 好的,我删除了 Go 部分。然后使用 C++。我不知道我必须如此具体。这是我关于stackoverflow的第二个问题。对于新手,我如何才能足够快且有意义地改进问题以提高投票率? :)

标签: c++ arrays search find


【解决方案1】:

如果我正确理解您的问题,您需要在数组中找到两个位置,第一个位置的所有数字都大于或等于 100,第二个位置的所有数字都小于或等于 1000 .

函数std::lower_boundstd::upper_bound 执行二进制搜索,旨在找到这样的范围。

对于数组,在C++ 中,我们通常使用std::vector 并使用一对迭代器 表示范围的开始和结束。

所以你可能需要这样的东西:

std::pair<std::vector<int>::iterator, std::vector<int>::iterator>
    find_range(std::vector<int>& v, int min, int max)
{
    auto begin = std::lower_bound(std::begin(v), std::end(v), min);

    // start searching after the previously found value
    auto end = std::upper_bound(begin, std::end(v), max);

    return {begin, end};
}

您可以像这样遍历该范围:

auto range = find_range(v, 100, 1000);

for(auto i = range.first; i != range.second; ++i)
    std::cout << *i << '\n';

您可以像这样从范围(慢)中创建一个新向量:

std::vector<int> selection{range.first, range.second};

【讨论】:

  • 能不能把返回的item数限制为10或者一些特定的值?
  • @Sam -- 返回到开始项和结束项所在的位置。
  • @Sam 不,它只是返回范围的开始和结束。但您可以从该范围内选择十个。
  • @Sam 相反。我认为这种方法非常快。我不知道有比 binary search (它使用)更快的定位排序值的方法。通过返回两个 迭代器,您还可以避免复制元素的成本。
【解决方案2】:

我的第一次尝试。

特点:

  • logN 时间复杂度

  • 创建一个数组切片,不复制数据

  • 第二次二分查找在第一次的基础上最小化搜索空间

可能的改进:

  • 如果 n 很小,则第二次二分搜索将是一种悲观化。最好简单地向前数 n 次。

 

#include <vector>
#include <cstdint>
#include <algorithm>
#include <iterator>
#include <iostream>

template <class Iter> struct range
{
    range(Iter first, std::size_t size) : begin_(first), end_(first + size) {}

    auto begin() const { return begin_; }
    auto end() const { return end_; }

    Iter begin_, end_;
};

template<class Iter> range(Iter, std::size_t) -> range<Iter>;

auto find_first_n_between(std::vector<std::int64_t>& vec, 
                          std::size_t n, 
                          std::int64_t from, std::int64_t to)
{
    auto lower = std::lower_bound(begin(vec), end(vec), from);
    auto upper = std::upper_bound(lower, end(vec), to);
    auto size = std::min(n, std::size_t(std::distance(lower, upper)));
    return range(lower, size);
}


int main()
{
    std::vector<std::int64_t> vec { 1,2,3,4,5,6,7,8,15,17,18,19,20 };
    auto slice = find_first_n_between(vec, 5, 6, 15);

    std::copy(std::begin(slice), std::end(slice), std::ostream_iterator<std::int64_t>(std::cout, ", "));
}

【讨论】:

  • 如何再次运行此脚本?我试过 -std=c++11 但它显示 script.cc:11:23: 警告:'begin' 函数使用'auto' 类型说明符,没有尾随返回类型 [默认启用] auto begin() const { return begin_; }
  • 我用c++17编译过
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 2017-12-27
  • 2015-11-26
相关资源
最近更新 更多