当前位置:   article > 正文

布隆过滤器详解 原理+实现 C++_布隆过滤器 哈希函数

布隆过滤器 哈希函数

布隆过滤器(bloom filter)


布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是由一个很长的bit数组和一系列哈希函数组成的。布隆过滤器可以用于检索一个元素是否在一个集合中
它是一种概率型数据结构,它能告知你一个元素大概率存在,或一定不存在。 ,常用与黑名单查找、垃圾邮件过滤、解决缓存穿透。



一、原理


假设布隆过滤器由20位二进制、3个哈希函数组成,每个元素经过哈希函数处理都能生成一个索引位置

  • 添加元素时,将每一个哈希函数生成的索引位置都设为1。

  • 查找元素时,如果有一个哈希函数生成的索引位置不为1,就代表100%不存在。如果每一个哈希函数生成的索引位置都为1,就代表存在(存在错误率)

在这里插入图片描述

在这里插入图片描述



布隆过滤器的误判率

此时table内并没有存入cat ,查询 cat ,1 3 7 位置全为1, 产生误判。布隆过滤器的误判率本质是由于hash冲突造成的。

在这里插入图片描述


推导

网上的推导过程很多,这里就直接给出结论

误判率 P 受到三个因素的影响:二进制个数 m、哈希函数个数 k、数据规模 n

在这里插入图片描述
误判率和数据规模都是我们手动给出,只要求二进制位的个数m、哈希函数的个数k,

在这里插入图片描述

例如:数据规模为1千万,误判率规定为千分之一。只需将其参数套入公式,就可得出m和k。

在这里插入图片描述



二、C++ 简单实现


int hashCode(int val) 		//提供整形、浮点、字符串的哈希值。
{
	return val;
}

int hashCode(float val)
{
	return  *(int*)(&val);
}

int hashCode(double val)
{
	unsigned long long tmp = *((long long*)&val);
	return ((tmp >> 32) ^ (tmp));
}

int hashCode(const string& val)
{
	int ret = 0;
	for (int i = 0; i < val.size(); i++)
	{
		//ret = ret * 31 + val[i]; 
		ret = (ret << 5) - ret + val[i];
	}

	return ret;
}


template<class T>
class BloomFilter
{
public:
	BloomFilter(int n, double p)
	{
		assert(n > 0 && p > 0 && p < 1);

		double ln2 = log(2);
		bitSize = -(n * log(p) / (ln2 * ln2));
		hashSize = bitSize * ln2 / n;

		int bitArrSize = (bitSize + 64 - 1) / 64;
		bitArr = new long long[bitArrSize];  //向上取整,还可以写成ceil(bitSize / 64)
		memset(bitArr, 0, bitArrSize * sizeof(long long));

	}

	~BloomFilter()
	{
		delete[] bitArr;
	}

	void put(const T& key)
	{
		assert(key);

		size_t hCode1 = hashCode(key);
		size_t hCode2 = hCode1 >> 16;

		for (int i = 1; i <= hashSize; i++)
		{
			int index = hCode1 + (i * hCode2);	//尽可能的使index分布均匀
			if (index < 0)						//防止index为负数
				index = ~index;

			index %= bitSize;					//防止越界

			int pos = index / 64;				//设置index位置为1
			int offset = index % 64;
			unsigned long long n = 1;
			bitArr[pos] = bitArr[pos] | (n << offset);
			int tmp = 1;
		}
	}

	bool contains(const T& key)
	{
		assert(key);

		size_t hCode1 = hashCode(key);
		size_t hCode2 = hCode1 >> 16;

		for (int i = `在这里插入代码片`1; i <= hashSize; i++)
		{
			int index = hCode1 + (i * hCode2);	//尽可能的使index分布均匀
			if (index < 0)						//防止index为负数
				index = ~index;

			index %= bitSize;					//防止越界

			int pos = index / 64;				//设置index位置为1
			int offset = index % 64;
			unsigned long long n = 1;

			if ((bitArr[pos] & (n << offset)) == 0)   //为0直接返回
				return false;
		}

		return true;
	}

private:
	int bitSize;		//bit位的个数
	int hashSize;		//hash函数个数
	long long* bitArr;	//bit数组
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107


三、应用场景

  1. 黑名单校验
  2. 快速去重
  3. 爬虫URL校验
  4. leveldb/rocksdb快速判断数据是否已经block中,避免频繁访问磁盘
  5. 解决缓存穿透


四、优缺点

优点

  1. 节省内存空间
  2. 插入和查询时间复杂度都为O(1)

缺点

  1. 布隆过滤器不支持删除
  2. 由于哈希冲突的原因,可能会出现假阳性
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号