查找积分值计数的最快方法(C ++)

时间:2016-11-19 22:38:46

标签: c++ performance c++11

我需要从无符号积分列表中找出每个出现值的出现次数。即如果通过序列[3,6,9,3,9]我会想要[{3,2},{6,1},{9,2}]。

值是随机32位无符号整数(范围为1到1,000,000,000)。结果可以存储在任何数据结构中(只要它们可以线性迭代)并且有序值是理想的,这是速度后的次要问题。

目前我有 -

T UniqueCount(std::vector<unsigned> &A)
{
    std::unordered_map<unsigned,unsigned> value_counts;

    for(unsigned val : A) {
        value_counts[val]++;
    }

    A.clear();

    ...
}

分析显示std :: unordered_map比std :: map更快。

有更好的方法吗? /更快的方式?值得注意的是,因为用例(count> 4)可以记录为4。

这是目前的一个瓶颈,所以虽然标准容器是首选的,但如果性能提升值得额外的维护成本,可以考虑定制。

2 个答案:

答案 0 :(得分:3)

如果您的值的范围合理(即,您执行我建议的内容时内存不足),您可以使用数组或vector,例如对于范围[0,max_value](未经测试,但你明白了):

// init
vector<int> counts(max_value + 1, 0);

// increment:
counts[value] ++;

或者您可以根据需要动态调整大小:

// init
vector<int> counts;

// increment:
if (value >= counts.size())
    counts.resize(value + 1, 0);
counts[value] ++;

如果范围合理但是为负数,则可以添加偏移量以使所有值均为非负数,或者为负数保留单独的向量并使用其绝对值。

否则,哈希地图几乎就要走了,所以你已经达到了极限 - 你可以继续尝试使用unordered_map,但提供一个不同的hash function可以提供更多为典型数据统一分配哈希值。

其他想法:

  • 并行化计数 - 在多个线程上计算向量的块数,并且a)在结束时将它们组合起来或b)使用原子增量计数器(例如Windows上的InterlockedIncrement)测试性能,尽管......你仍然需要线程安全插入新值,所以可能坚持使用A)。不能告诉你a或b是否会更快,你必须测试。使用线程池或其他一些预先创建的线程,因为您可能不希望每次都有启动和停止线程的全部开销。

  • 如果长时间运行相同的值或许多短期运行,您可以将映射迭代器缓存到先前的值。然后,如果您要查看的值与迭代器的重用次数相同,并保存自己的哈希查找。虽然我不知道这有什么不同,但我不知道,你必须尝试使用​​你的特定数据集。

我真的想不起其他的东西。

答案 1 :(得分:3)

在我的系统(Win10 x64,MSVC daily package x64版本构建)上,使用输入向量中的100,000个随机未排序值进行测试,以下使用std::sort + std::adjacent_find执行~10ms vs .~ {27}使用std::unordered_map和@ krzaq的答案中的代码(现在在OP中):

std::vector<std::pair<unsigned, unsigned>> unique_count(std::vector<unsigned>& a) {
    auto it = begin(a);
    auto const last = end(a);

    std::vector<std::pair<unsigned, unsigned>> value_counts;
    std::sort(it, last);
    while (it != last) {
        auto const prev = it;
        it = std::adjacent_find(it, last, std::not_equal_to<unsigned>{});
        if (it != last) {
            ++it;
        }
        value_counts.emplace_back(*prev, static_cast<unsigned>(it - prev));
    }
    return value_counts;
}

Online Demo

经验教训:缓存一致性通常会超出算法的复杂性。