【问题标题】:STL Set: insert two million ordered numbers in the most efficient mannerSTL Set:以最有效的方式插入两百万有序数
【发布时间】:2010-10-19 17:23:16
【问题描述】:

对于下面的批量插入,由于输入是有序的,是否有任何(轻微的)优化?

set<int> primes;
for ( int i = 2; i <= 2000000; i++ ) {
    primes.insert(i);
}
// then follows Sieve of Eratosthenes algorithm

新的改进,速度提高一倍:

set<int> primes;
for ( int i = 2; i <= 2000000; i++ ) {
    primes.insert(primes.end(), i);
}
// then follows Sieve of Eratosthenes algorithm

【问题讨论】:

    标签: c++ stl insert set


    【解决方案1】:

    来自http://www.cplusplus.com/reference/stl/set/set/

    vector<int> numbers;
    for (int i=2; i<=2000000; ++i)
        numbers.push_back(i);
    
    set<int> primes(numbers.begin(), numbers.end());
    

    这应该有 O(n) 复杂度,而你的复杂度是 O(n*log(n))。

    引用的链接说,当您使用基于迭代器的构造函数时,如果迭代器已排序,那么您将获得线性。但是,我没有看到任何关于如何显式通知构造函数它是排序迭代器的明确内容。


    为了更简洁,我宁愿使用 boost 的计数迭代器。这将其缩短为:

    set<int> primes(
        boost::counting_iterator<int>(0),
        boost::counting_iterator<int>(200000));
    

    【讨论】:

    • 更好的是,只将实际的素数放入您的集合中,而不是每个数字。
    • @luke:他的代码的下一步就是这样做。或者至少将集合过滤为素数。
    • 另外,您确切知道该向量到最后会有多大。您应该分配一次(通过构造函数)而不是多次分配,这会真正影响性能。
    • @sharth,您的vector-to-set 代码的运行时改进了 100%。虽然,我不知道为什么,因为逻辑上没有新的事情发生。然后,这个跳出来了:primes.insert(primes.end(), i);
    • -1 @sharth:代码不正确。 vector&lt;int&gt; v(2000000) 将创建一个包含 2M 个零的向量。以下push_back 操作将在 2M 个零之后添加前 2M 个数字。即使这是正确的(将push_back 更新为numbers[i]=i,最后一步set&lt;int&gt; primes(numbers.begin(), numbers.end()) 的成本与将它们添加暗示insertend 迭代器的成本完全相同。counting_iterator 也是如此,它的性能并不比原来的insert有提示。
    【解决方案2】:

    有一个可用的insert 方法的重载版本,它将迭代器作为第一个参数。该迭代器用作提示,即比较将从该迭代器开始。因此,如果您将正确的迭代器作为提示传递,那么您应该对集合进行非常有效的插入操作。有关详细信息,请参阅here。我相信在您的情况下,numbers.end() -1 将是一个不错的选择。

    【讨论】:

      【解决方案3】:

      如果您从候选ints 的全部范围开始,我根本不会使用set,我会使用vector&lt;bool&gt;,将它们全部初始化为false,并标记目标(素数)为true,因为它们所在的位置。

      vector<bool> candidates(200000);
      

      您可以使用当前候选 int - 1(候选范围 = 1..200000)对此进行索引。然后使用find 进行迭代以定位真实值,通过使用偏移量与begin() 转换为int

      • 内存分配总数 - 1。
      • 总内存使用量 25000 字节(vector&lt;bool&gt; 是一种特殊情况,请参阅@Greg Rogers 的评论)vs >= 800000 for set&lt;int&gt;,减少 BTree 开销。
      • 所有元素的访问都是通过指针算法实现的。

      【讨论】:

      • 对于埃拉托色尼筛算法,set 是我的第一个想法,可能会“工作”,尽管我还不知道它的开销是否会起作用。所以,再想一想, vectorbools 是一个非常好的选择。我会同时实现并看看。谢谢。
      • @Derek - 我相信这会更好地扩展 - 随着上限大小的增加,管理集合的开销会变得更糟。 vector 可能允许在同一硬件上实现更高的上限 N 绝对值,尽管它确实依赖于大小为 N 的连续内存 - 如果这成为问题。对于范围顶部的低值,setvector 都可以正常工作。我会对你的结果感兴趣,如果你愿意回报的话。
      • 对于我测试的所有输入大小,vector(与set 相比)解决方案的运行速度约为 4.5 倍。再次感谢。
      • 这是对埃拉托色尼筛的最佳建议。但是,如果范围在编译时是固定的,那么我倾向于使用 std::bitset&lt; 200000 &gt; candidates;
      • vector&lt;bool&gt; 是一种将位紧密打包到每个字节中的特化——因此分配的内存实际上是 200000 / 8 = 40000。
      【解决方案4】:

      这里有一些:

      1. 实现一个自定义 stl 分配器,预先进行内存预留。

      2. 如果使用矢量方案,请调用reserve。

      3. 如果您只是要实现筛子,请使用(或实现)boost counting iterator,并且只将结果存储在一个向量中,该向量会调用 Reserve。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        • 1970-01-01
        • 2015-04-02
        • 2018-08-18
        • 1970-01-01
        • 2015-09-30
        相关资源
        最近更新 更多