在内存中判断海量数据的唯一性
通过布隆过滤算法来实现了
假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制位全部设置为零。对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, ...,F8) 产生八个信息指纹(f1, f2, ..., f8)。再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, ...,g8。现在我们把这八个位置的二进制位全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。
当 k = ln(2)* m/n (k是hash函数个数,m是bit数组的长度,n是加入值的个数)时出错的概率是最小的。
例如:哈希函数个数k取10,位数组大小m设为字符串个数n的20倍时,false positive发生的概率是0.0000889 ,这个概率基本能满足网络爬虫的需求了。
假如k=10,总共有1亿条记录(即n=100 000 000),则需要的字节为m=1.86G(即m=n*20=100 000 000 *20=2 000 000 000=1.86G);
以下是C#的布隆算法处理类:
public class BloomFilter<T>
{
private BitArray _bitArray = null;
private int _count = 0;//n:记录的总条数
private int _hashcount = 1;//k:hash函数的个数
///<summary>
///
///</summary>
///<param for="size">m:bit数组的长度</param>
///<param for="hashcount">k:是hash函数个数</param>
public BloomFilter(int size, int hashcount)
{
_bitArray = new BitArray(size, false);
_hashcount = hashcount;
}
public void Add(T item)
{
int h1 = item.GetHashCode();
int h2 = Hash(h1.ToString());
bool result = false;
unchecked
{
h1 = (int)(((uint)h1) % _bitArray.Count);
h2 = (int)(((uint)h2) % _bitArray.Count);
}
for (int i = 0; i < _hashcount; i++)
{
if (!_bitArray[h1])
{
_bitArray[h1] = result = true;
}
unchecked
{
h1 = (int)((uint)(h1 + h2) % _bitArray.Count);
h2 = (int)((uint)(h2 + i) % _bitArray.Count);
}
}
if (result)
{
_count++;
}
}
public bool Contains(T item)
{
int h1 = item.GetHashCode();
int h2 = Hash(h1.ToString());
unchecked
{
h1 = (int)(((uint)h1) % _bitArray.Count);
h2 = (int)(((uint)h2) % _bitArray.Count);
}
for (int i = 0; i < _hashcount; i++)
{
if (_bitArray[h1] == false)
{
return false;
}
unchecked
{
h1 = (int)((uint)(h1 + h2) % _bitArray.Count);
h2 = (int)((uint)(h2 + i) % _bitArray.Count);
}
}
return true;
}
protected int Hash(T item)
{
int hashcode = item.GetHashCode();
hashcode = Hash(hashcode.ToString());
return hashcode;
}
/// <summary>
/// 字符串Hash函数(AP Hash Function)
/// </summary>
/// <param name="str">需要Hash的字符串</param>
/// <returns></returns>
protected int Hash(string str)
{
long hash = 0;
for (int i = 0; i < str.Length; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ str[i] ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5)));
}
}
unchecked
{
return (int)hash;
}
}
/// <summary>
/// 记录的总条数
/// </summary>
public int Count
{
get
{
return _count;
}
}
/// <summary>
/// 返回BloomFilter中的bit数组的长度
/// </summary>
public int SizeBytes
{
get
{
return _bitArray.Length;
}
}
}
参考:
https://luzhijun.github.io/2016/09/02/%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86/