是否有任何概率数据结构可以减少大量计数器的空间复杂性?

时间:2016-04-12 23:07:53

标签: algorithm data-structures hashtable counter counting

基本上我需要跟踪大量的计数器。我可以按名称递增或递减每个计数器。最简单的方法是使用哈希表,使用counter_name作为key,将其对应的count作为value的{​​{1}}。

计数器不需要100%准确,key的近似值很好。所以我想知道是否有任何概率数据结构可以将N个计数器的空间复杂度降低到低于O(N),有点类似于HyperLogLog通过仅给出近似结果来减少计数N个项目的内存需求。有什么想法吗?

3 个答案:

答案 0 :(得分:2)

在我看来,你要找的东西是Count-min sketch

  

读取元素流a1,a2,a3,...,可以有a的元素   很多重复的元素,在任何时候它都会给你答案   以下问题:到目前为止你看过多少个ai元素。

基本上你的独特元素可以被射入你的计数器。 Countmin sketch允许您调整参数以交换记忆以获得准确性。

P.S。我描述了一些其他流行的probabilistic data structures here

答案 1 :(得分:1)

Stefan Haustein的说法是正确的,名字可能比计数器占用更多的空间,你可以按照他的建议对某些名字进行优先排序,但如果没有,你可以考虑如何最好地存储这些名字。如果它们相当短(例如8个字符或更少),您可以考虑使用封闭的散列表将它们直接存储在存储桶中。如果它们很长,你可以将它们连续存储(NUL终止)在一块内存中,并在哈希表中将偏移存储到它们的第一个字符的那个块中。

对于计数器本身,可以使用概率方法来节省空间,如下所示:

template <typename T, typename Q = unsigned>
class Approx_Counter
{
  public:
    Approx_Counter() : n_(0) { }

    Approx_Counter& operator++()
    {
        if (n_ < 2 || rand() % (operator Q()) == 0)
            ++n_;
        return *this;
    }

    operator Q() const { return n_ < 2 ? n_ : 1 << n_; }

  private:
    T n_;
};

然后你可以使用例如Approx_Counter<unsigned char, unsigned long>。如果你愿意,可以换掉rand()用于C ++ 11生成器。

这个想法很简单:

  • n_0时,++绝对不会被调用
  • n_1时,++肯定已被调用一次
  • n_ >= 2时,表示++ 可能已被调用约2 n _

为了使最后一个含义与实际进行的++调用的数量保持一致,每次调用都有1比2 n _ 再次实际递增n_的机会。 / p>

只需确保您的rand()或替代值返回的值远远大于您要跟踪的最大计数器值,否则您将过于频繁地获得rand() % (operator Q()) == 0并且不正确地增加。

也就是说,如果你有一个指针或偏移量,那么一个较小的计数器并没有多大帮助,所以你也想要将计数器挤进水桶,另一个原因就是喜欢你自己的封闭散列实现,如果你真的需要收紧内存使用但想要坚持使用哈希表(trie是另一种可能性)。

以上在计数器空间中仍然是O(N),只是具有较小的常数。真的&lt;在O(N)选项中,您需要考虑密钥是否/如何相关,这样增加计数器可能会合理地影响多个密钥。到目前为止,您还没有向我们提供任何见解。

答案 2 :(得分:0)

名称可能比计数器占用更多空间。

拥有固定数量的计数器并且只保留计数最高的计数器,加上某种LRU机制以允许新计数器升至顶部怎么样?我想这真的取决于你的用例...