赞
踩
问题:
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
问题分析:
时间复杂度角度:
什么是位图?
位图,就是用每一bit位来存放某种状态,为1,代表存在,为0
代表不存在适,用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。利用位图处理该问题的思路就是压缩内存成本,是利用哈希思想建立映射的方案可行
位图能解决该问题的大概计算
只粗略计算主干:1 byte 可建立8个数的映射关系(闭散列),那么40亿个数需要 40亿 / (1024 * 1024* 1024 * 8 ) 约等于0.5GB内存。
位图应用总结
位图的模拟实现
注意点:
1.运算符优先级
2.运算逻辑的控制
在.h文件中:
#pragma once #include<vector> #include<iostream> #include<string> using namespace std; namespace myBitSet { // 非类型模板参数特化的应用 N的含义为N个bit位的位图 template<size_t N> class bitset { private: //成员变量 std::vector<char> _bits; public: bitset() { // 一个char占8个比特位 _bits.resize(N / 8 + 1, 0); } //大概理解为建立映射的功能 void set(size_t x) { // x 映射的比特位在第几个char对象 size_t i = x / 8; // x 在char的第几个比特位 size_t j = x % 8; //# 表示可能位0也可能为1 // 举例j = 3 // 原第i个char: ######## // (1 << j) : 00001000 //运算结果: ####1### _bits[i] |= (1 << j); //注意运算符优先级 } //类似与删除映射的功能 void reset(size_t x) { size_t i = x / 8; size_t j = x % 8; _bits[i] &= (~(1 << j)); } //查找位是否建立映射的功能 bool test(size_t x) { size_t i = x / 8; size_t j = x % 8; return _bits[i] & (1 << j); } //测试方案 void test_can_do() { bitset<100> bs; bs.set(33); bs.set(57); cout << bs.test(33) << endl; cout << bs.test(57) << endl; bs.reset(33); cout << bs.test(33) << endl; cout << bs.test(57) << endl; } }; }
位图的缺点?
只能进行整数的海量数据处理。位图为降低空间损耗,建立的映射方案就是数字N的状态用第N个bit位来标识状态。不再需要区花多余的空间去解决哈希冲突,扩容等问题。
什么是布隆过滤器?
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
布隆过滤器工作原理
布隆过滤器的映射建立与查找
布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。
注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。
布隆过滤器删除
布隆过滤器不能直接支持删除工作,因为不同元素在建立映射的时候,多个映射位完全相同的概率很小,但存在相同映射位的概率大,在删除一个元素的映射位时,可能会影响其他元素。
布隆过滤器优点
布隆过滤器缺陷
布隆过滤器模拟实现
在.文件中:
//布隆过滤器/ #pragma once #include<vector> #include<iostream> #include<bitset> #include<string> using namespace std; // 将字符串转为可进行整形映射的四种较优方法 //指效率较优秀 struct BKDRHash { size_t operator()(const string& s) { // BKDR size_t value = 0; for (auto ch : s) { value *= 31; value += ch; } return value; } }; struct APHash { size_t operator()(const string& s) { size_t hash = 0; for (long i = 0; i < s.size(); i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5))); } } return hash; } }; struct DJBHash { size_t operator()(const string& s) { size_t hash = 5381; for (auto ch : s) { hash += (hash << 5) + ch; } return hash; } }; struct JSHash { size_t operator()(const string& s) { size_t hash = 1315423911; for (auto ch : s) { hash ^= ((hash << 5) + ch + (hash >> 2)); } return hash; } }; template<size_t M, class K = string, class HashFunc1 = BKDRHash, class HashFunc2= APHash, class HashFunc3= DJBHash, class HashFunc4= JSHash> class BloomFilter // 关键字: Bloom 布隆过滤器 { private: //成员变量 bitset<M> _bs; public: void Set(const K& key) { //如果这样写则会被认为是强制类型转换,类名() 是调用该类构造函数的格式 //size_t hash1 = HashFunc1(key) % M; //所以HashFunc1()是有个匿名对象的 size_t hash1 = HashFunc1()(key) % M; size_t hash2 = HashFunc2()(key) % M; size_t hash3 = HashFunc3()(key) % M; size_t hash4 = HashFunc4()(key) % M; _bs.set(hash1); _bs.set(hash2); _bs.set(hash3); /*_bs.set(hash4);*/ } bool Test(const K& key) { //这样逐个判断,只要有一个不符合就立马返回结果符合效率最高 size_t hash1 = HashFunc1()(key) % M; if (_bs.test(hash1) == false) { return false; } size_t hash2 = HashFunc2()(key) % M; if (_bs.test(hash2) == false) { return false; } size_t hash3 = HashFunc3()(key) % M; if (_bs.test(hash3) == false) { return false; } size_t hash4 = HashFunc4()(key) % M; if (_bs.test(hash4) == false) { return false; } return true; //如果判断为false,即不存在,结果是100%正确的 //结果为 true 存在误判的可能性 } //布隆过滤器不存在该选项,因为可能误删其他存在的映射关系 bool Reset(const K& key); void test_can_do() { BloomFilter<43> bf; string a[] = { "苹果", "香蕉", "西瓜", "111111111", "eeeeeffff", "草莓", "休息", "继续", "查找", "set" }; for (auto& e : a) { bf.Set(e); } for (auto& e : a) { cout << bf.Test(e) << endl; } cout << endl; cout << (bf.Test("芒果")) << endl; cout << bf.Test("string") << endl; cout << bf.Test("ffffeeeee") << endl; cout << bf.Test("31341231") << endl; cout << bf.Test("ddddd") << endl; cout << bf.Test("3333343") << endl; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。