我收到一组无序的Int32值,需要跟踪我收到的不同值的计数。
我的想法是将Int32值添加到HashSet<Int32>
。根据HashSet的行为,根本不会添加重复的条目。
我是否正确理解集合成员资格是基于GetHashCode()并且Int32的哈希码是数字本身?
是否存在更多CPU或更高内存效率的方法?
更新
数据流相当大。简单地使用Linq来迭代流以获得不同的计数并不是我所追求的,因为这将涉及第二次迭代流。
答案 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>
的原始方法最适合我的情况。
重新迭代流以获得不同的计数效率不高。