.NET中不同Int32值的计数

时间:2012-06-27 22:07:49

标签: .net

我收到一组无序的Int32值,需要跟踪我收到的不同值的计数。

我的想法是将Int32值添加到HashSet<Int32>。根据HashSet的行为,根本不会添加重复的条目。

我是否正确理解集合成员资格是基于GetHashCode()并且Int32的哈希码是数字本身?

是否存在更多CPU或更高内存效率的方法?

更新

数据流相当大。简单地使用Linq来迭代流以获得不同的计数并不是我所追求的,因为这将涉及第二次迭代流。

5 个答案:

答案 0 :(得分:4)

假设您有某种IEnumerable<int>,您可以执行以下操作:

int count = stream.Distinct().Count();

  

我是否理解正确的集合成员资格基于GetHashCode()

不完全。 HashSet中的成员身份基于GetHashCode和等同性检查的组合。通常,两个对象可以具有相同的哈希码但不相等。虽然int不可能发生。

  

并且Int32的哈希码本身就是数字?

是的,这是正确的。

  

是否存在更多CPU或更高内存效率的方法?

如果您知道您的整数将在一个小范围内,您可以使用位图有效地存储您所看到的内容。例如,如果您的范围为1,000,000,则可以存储您在1,000,000位中看到的内容。在索引n处设置为1的位意味着您已经看到整数n。下面是一些示例代码,显示了实现此目的的一种方法:

void Main()
{
    int max = 1000000;

    IEnumerable<int> stream = GetStream(max);

    int count = DistinctCount(stream, max);
    int count2 = stream.Distinct().Count();
    Debug.Assert(count == count2);
}

int DistinctCount(IEnumerable<int> stream, int max)
{
    int[] seen = new int[max / 32];
    foreach (int x in stream)
    {
        seen[x / 32] |= 1 << (x % 32);
    }

    int count = 0;
    foreach (uint s in seen)
    {
        uint t = s;
        while (t > 0)
        {
            if (t % 2 == 1) { count++; }
            t /= 2;
        }
    }
    return count;
}

IEnumerable<int> GetStream(int max)
{
    List<int> stream = new List<int>();
    Random random = new Random();
    for (int i = 0; i < 2000000; ++i)
    {
        stream.Add(random.Next(max));
    }
    return stream;
}

答案 1 :(得分:1)

有人认为,如果您有非常大量数据流(数百万到数十亿),则使用Bloom filter。这将使您能够在流式传输数据时确定大致计数,如果您需要精确计数,则可以离线处理。

合理的C#实现在这里:http://bloomfilter.codeplex.com/

答案 2 :(得分:1)

不太了解您的域名,但有一些算法可以使用非常小的内存和处理来计算大型集合的基数。

我在我的一个项目中使用HyperLogLog。我使用它来计算数百万个不同的项目,使用低至8KB的内存,误差为1%。

这是一篇描述它的论文:

http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf

我已经用Java和Python实现了它。 Python版本是开源的,算法相当小。看看:

https://github.com/juanplopes/hyperloglog.py/blob/master/hyperloglog.py

答案 3 :(得分:0)

我假设您以块的形式接收值,一次一个int到一堆int。

鉴于此,最简单的事情可能是最好的,我也使用哈希。但是,我不知道如何使用HashSet。如果您想要计算不同的值,则只能获得找到的值

Dictionary<int,int> _countHash = new Dictionary<int,int>();
void moreIntsArrived(IEnumerable<int> bunch)
{
   foreach(var value in bunch)
   {
       if (_countHash.ContainsKey(value))
       {
             _countHash[value] += _countHash[value];
       }
       else
       {
             _countHash[value] = 0;
       }
   }
}

然而,做Mr Hansleman suggests, measure it

在执行ContainsKey检查之间可能存在折衷,只有在未找到密钥时才会触及异常, IF 您的流足够大以停止获取新的唯一值

void moreIntsArrived(IEnumerable<int> bunch)
{
   foreach(var value in bunch)
   {
       try
       {
            int c = _countHash[value];
             _countHash[value] = c + 1;
       }
       catch(KeyNotFoundException)
       {
             _countHash[value] = 0;
       }
   }
}

然后又有了Dictionary :: TryGetValue()方法,但它取决于里面的内容:-) 使用来源

答案 4 :(得分:0)

我很欣赏其他答案,但发现使用HashSet<T>的原始方法最适合我的情况。

重新迭代流以获得不同的计数效率不高。