【问题标题】:How to efficiently look up elements in a large vector如何有效地查找大向量中的元素
【发布时间】:2015-05-27 10:19:45
【问题描述】:

我有一个大小为(90,000 * 9,000)vector<unsigned>。我需要多次查找该向量中是否存在元素?

为此,我使用std::sort() 以排序形式存储向量,然后使用std::binary_search() 在向量中查找元素。但是在使用perf 进行分析时,我发现在vector<unsigned> 中查找元素是最慢的操作。

有人可以在C/C++ 中建议一些data-structure,我可以用它来有效地查找(90,000 * 9,000) 元素向量中的元素。

我只执行一次插入(批量插入)。其余时间我只执行查找,所以这里的主要开销是查找。

【问题讨论】:

  • 你是否多次寻找同一个元素?如果是这样,您可以缓存结果。
  • 向量有什么类型的数据?
  • 为了提供有意义的答案,我们需要知道其他限制条件——例如。插入(如果使用,删除)操作的预期性能。
  • 如果您能以这种方式组织数据,那么 trie 应该会更快。
  • 二分查找最多需要 log_{2}(81e7) ~= 30 步。你确定尺寸是你的问题吗?

标签: c++ c vector


【解决方案1】:

在 40 亿个可能的值中,您有 8.1 亿个值(假设为 32 位 unsigned)。这是总范围的 1/5,使用 3.2 GB。这意味着您实际上最好使用具有 40 亿位的std::vector<bool>。这使您可以在更少的空间 (0.5 GB) 中进行 O(1) 查找。

(理论上,unsigned 可能是 16 位。unsigned long至少是 32 位,std::uint32_t 可能是你想要的)

【讨论】:

  • 我无法理解如何使用 std::vector
  • @StegVerner:很简单:你从一个包含UNIT_MAX 值的向量开始,全都是假的。要插入 42,请设置 v[42]=true。要删除 42,v[42]=false。要检查 42 是否存在,if (v[42])
【解决方案2】:

根据向量的实际数据结构,contains 操作可能采用O(n)O(1)。通常,如果向量由关联数组或链表支持,则为O(N),在这种情况下,contains 将在最坏的情况下进行全扫描。您已经通过排序和使用二分搜索(O(log (N)))减轻了全面扫描。 Log N 非常复杂,只有 O(1) 更好。所以你的选择是:

  • 缓存项目的查找结果,如果同一元素有多次重复,这可能是一个很好的折衷方案
  • 将向量替换为另一个具有高效contains 操作的数据结构,例如基于hashtableset 的数据结构。请注意,您可能会失去其他功能,例如物品排序
  • 使用两种数据结构,一种用于contains 操作,而原始向量则用于任何用途
  • 使用提供折衷方案的第三种数据结构,例如与bloom filter 配合良好的数据结构

【讨论】:

  • 问题指定binary_search,既不是O(1)也不是O(N),而是O(log N)。此外,vector 由连续内存支持,而不是关联数组或链表。
  • 不,问题没有这么说。问题是,我问:“我需要多次查找某个元素是否存在于这个向量中?”。二进制搜索是 OP 尝试的解决方案,实际上二进制搜索是 O(logN),但他可以使用哈希表在 O(1) 中执行此操作,这是我建议的选项之一。
【解决方案3】:

但是在使用 perf 进行分析时,我发现在 向量是最慢的操作。

这是您需要的一半信息,另一半是“与其他算法/容器相比有多快”?也许使用std::vector<> 实际上是最快的,或者可能是最慢的。要找到您必须对一些不同的设计进行基准测试/分析。

例如,以下是在 1000x9000 大小的容器上使用随机整数的非常天真的基准测试(我会在地图的较大尺寸上遇到段错误,假设是 32 位内存的限制)。

如果您需要非唯一整数的计数:

  • std::vector<unsigned> = 500 毫秒
  • std::map<unsigned, unsigned> = 1700 毫秒
  • std::unordered_map<unsigned, unsigned> = 3700 毫秒

如果您只需要测试是否存在唯一整数:

  • std::vector<bool> = 15 毫秒
  • std::bitset<> = 50 毫秒
  • std::set<unsigned> = 350 毫秒

请注意,我们对确切的值不太感兴趣,而是对容器之间的相对比较感兴趣。 std::map<> 相对较慢,考虑到动态分配的数量和所涉及数据的非本地性,这并不奇怪。到目前为止,位集是最快的,但如果需要非唯一整数的计数,则不起作用。

我建议使用您的确切容器大小和内容进行类似的基准测试,这两者都可能会影响基准测试结果。毕竟std::vector<> 可能是最好的解决方案,但现在您有一些数据来支持该设计选择。

【讨论】:

    【解决方案4】:

    如果您从 c++11 开始不需要遍历集合(以排序方式),您可以使用std::unordered_set<yourtype>,您需要做的就是提供获取yourtype 的散列和相等信息的集合方式.访问集合元素的时间在这里摊销为 O(1),与排序向量不同的是 O(log(n))。

    【讨论】:

    • 我想知道 90,000 * 9000 的要求,以及这是否意味着向量被用作二维数组 - 在这种情况下,元素的索引可能就是所找到的。
    • @marko 是的,在这种情况下你是对的,可能值得将 unordered_map 模板化参数更改为例如std::unordered_map<std::pair<int, int>, sometype>
    • @marko 使用的向量是一维数组。 * 代表乘法..:)
    • 我认为你不想要 map 而是 set - 没有键/值分离。
    • 使用boost::multi_index 也可能是同时使用random_access_indexhashed_index 作为索引的选项。您在索引类型之间获得 O(1) 转换 - 所以在这种情况下,我们将使用 hash_index 来查找接近 O(1) 的项目并转换回数组索引(也是 O(1))。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 2012-12-05
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 2015-04-30
    相关资源
    最近更新 更多