Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;_C++_Algorithm_Stl_Set Intersection - Fatal编程技术网

C++ C++;

C++ C++;,c++,algorithm,stl,set-intersection,C++,Algorithm,Stl,Set Intersection,我有一组std::set。我想以最快的方式找到集合中所有集合的交集。集合中的集合数通常非常小(~5-10),每个集合中的元素数通常少于1000个,但有时可能高达10000个左右。但是我需要以尽可能快的速度进行成千上万次的交叉。我尝试对以下几种方法进行基准测试: 初始复制第一个集合的std::set对象中的就地交点。然后,对于后续的集合,它迭代自身的所有元素和集合的第i个集合,并根据需要从自身移除项 使用std::set_intersection到临时std::set中,将内容交换到当前集,然后再

我有一组
std::set
。我想以最快的方式找到集合中所有集合的交集。集合中的集合数通常非常小(~5-10),每个集合中的元素数通常少于1000个,但有时可能高达10000个左右。但是我需要以尽可能快的速度进行成千上万次的交叉。我尝试对以下几种方法进行基准测试:

  • 初始复制第一个集合的
    std::set
    对象中的就地交点。然后,对于后续的集合,它迭代自身的所有元素和集合的第i个集合,并根据需要从自身移除项
  • 使用
    std::set_intersection
    到临时
    std::set
    中,将内容交换到当前集,然后再次找到当前集与下一集的交集并插入临时集,依此类推
  • 手动迭代所有集合的所有元素,如1),但使用
    向量
    作为目标容器,而不是
    std::set
  • 与4中相同,但使用
    std::list
    而不是
    向量
    ,怀疑
    列表
    将提供从中间更快的删除
  • 使用散列集(
    std::unordered_set
    )并检查所有集中的所有项

  • 事实证明,当每个集合中的元素数较少时,使用
    向量
    稍微快一点,而对于较大的集合,
    列表
    稍微快一点。就地使用
    set
    比两者都慢,其次是
    set\u交集
    和散列集。是否有更快的算法/数据结构/技巧来实现这一点?如果需要,我可以发布代码片段。谢谢

    您可能希望尝试对std::set_intersection()进行泛化:算法是对所有集合使用迭代器:

  • 如果任何迭代器已到达其相应集合的
    end()
    ,则完成。因此,可以假设所有迭代器都是有效的
  • 将第一个迭代器的值作为下一个候选值
    x
  • 遍历迭代器列表,并找到至少与
    x
    一样大的第一个元素
  • 如果该值大于
    x
    则将其作为新的候选值,并按迭代器顺序再次搜索
  • 若所有迭代器都在value
    x
    上,那个么您找到了交集的一个元素:记录它,递增所有迭代器,重新开始

  • Night是个很好的顾问,我想我可能有个主意;)

    • 如今,内存比CPU慢得多,如果所有数据都放在一级缓存中没有什么大不了的,但它很容易溢出到二级缓存或三级缓存:5组1000个元素已经是5000个元素,这意味着5000个节点,一组节点至少包含3个指针+对象(即,32位机器上至少包含16个字节,64位机器上至少包含32个字节)=>这至少是80k内存,最近的CPU只有32k用于L1D,因此我们已经扩展到L2
    • 前面的事实由于集合节点可能分散在内存中而不是紧密地打包在一起的问题而变得更加复杂,这意味着缓存线的一部分充满了完全不相关的内容。这可以通过提供一个使节点彼此靠近的分配器来缓解
    • CPU在顺序读取(它们可以在您需要内存之前预取内存,这样您就不用等待内存)方面比随机读取要好得多,这一事实进一步加剧了这种情况(不幸的是,树结构会导致非常随机的读取)
    这就是为什么在速度很重要的地方,向量(或者可能是deque)是非常好的结构:它们能很好地利用内存。因此,我绝对建议使用
    向量
    作为我们的中介结构;尽管需要注意的是,只能从末端插入/删除,以避免重新定位

    所以我想到了一个非常简单的方法:

    #include <cassert>
    
    #include <algorithm>
    #include <set>
    #include <vector>
    
    // Do not call this method if you have a single set...
    // And the pointers better not be null either!
    std::vector<int> intersect(std::vector< std::set<int> const* > const& sets) {
        for (auto s: sets) { assert(s && "I said no null pointer"); }
    
        std::vector<int> result; // only return this one, for NRVO to kick in
    
        // 0. Check obvious cases
        if (sets.empty()) { return result; }
    
        if (sets.size() == 1) {
            result.assign(sets.front()->begin(), sets.front()->end());
            return result;
        }
    
    
        // 1. Merge first two sets in the result
        std::set_intersection(sets[0]->begin(), sets[0]->end(),
                              sets[1]->begin(), sets[1]->end(),
                              std::back_inserter(result));
    
        if (sets.size() == 2) { return result; }
    
    
        // 2. Merge consecutive sets with result into buffer, then swap them around
        //    so that the "result" is always in result at the end of the loop.
    
        std::vector<int> buffer; // outside the loop so that we reuse its memory
    
        for (size_t i = 2; i < sets.size(); ++i) {
            buffer.clear();
    
            std::set_intersection(result.begin(), result.end(),
                                  sets[i]->begin(), sets[i]->end(),
                                  std::back_inserter(buffer));
    
            swap(result, buffer);
        }
    
        return result;
    }
    
    #包括
    
    #显然,我不能保证它的速度。

    问题实际上取决于你是否需要找到许多共同的元素,因为这会改变你能想到的“最佳”结构。例如,第六种方法可以是简单地使用and
    std::unordered_map
    并计算每个元素的出现次数。元素总数是O(N)。然后,您只需选取总数等于集合数的元素,即不同元素数中的O(M)。“不知道它的表现会有多好。”马蒂厄姆说。我懂了。我将尝试一下,尽管我怀疑,由于散列和其他开销,它不会比std::list快。谢谢@马蒂厄姆。此方法将以未排序的顺序给出结果集。幸运的是,我有两个用例,一个需要按顺序排序的结果,另一个不需要。如果这个方法相当快,我至少可以在交叉点不需要排序的情况下使用它。@MatthieuM。我尝试了这种方法,对于我的数据,这只比我的方法5(使用
    unordered\u set
    )稍微快一点。你可以试试。最坏情况下的线性(如果集合的元素基本相同,则无法避免这种情况),但如果交集很小,则速度会快得多。毕竟,当使用
    std::set
    时,我不建议使用
    std::find_if
    std::set
    功能的
    std::lower_bound
    std::upper_bound
    通常更快。@MatthieuM。不是在这种情况下,
    find_if
    平均不需要推进两个以上的元素,因此是O(1),而