赞
踩
目录
3.3 附用的unordered_set、unordered_map、OpenHash:
后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
1.1.1 位图概念
1. 面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】
分析:不能使用set/unordered_set,因为内存不够。也不能使用外排序+二分查找,因为不能很好的支持快速随机访问!
1. 遍历,时间复杂度O(N)
2. 排序(O(NlogN)),利用二分查找: logN
3. 位图解决
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一
个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。比如:
直接定址法哈希——用一个比特位标识映射值在不在!
2. 位图概念
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用
来判断某个数据存不存在的。
1.1.2 位图的实现
class bitset { public: bitset(size_t bitCount) : _bit((bitCount>>5)+1), _bitCount(bitCount) {} // 将which比特位置1 void set(size_t which) { if(which > _bitCount) return; size_t index = (which >> 5); size_t pos = which % 32; _bit[index] |= (1 << pos); } // 将which比特位置0 void reset(size_t which) { if(which > _bitCount) return; size_t index = (which >> 5); size_t pos = which % 32; _bit[index] &= ~(1<<pos); } // 检测位图中which是否为1 bool test(size_t which) { if(which > _bitCount) return false; size_t index = (which >> 5); size_t pos = which % 32; return _bit[index] & (1<<pos); } // 获取位图中比特位的总个数 size_t size()const{ return _bitCount;} // 位图中比特为1的个数 size_t Count()const { int bitCnttable[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; size_t size = _bit.size(); size_t count = 0; for(size_t i = 0; i < size; ++i) { int value = _bit[i]; int j = 0; while(j < sizeof(_bit[0])) { unsigned char c = value; count += bitCntTable[c]; ++j; value >>= 8; } } return count; } private: vector<int> _bit; size_t _bitCount; };1.1.3 位图的应用
1. 快速查找某个数据是否在一个集合中
2. 排序 + 去重
3. 求两个集合的交集、并集等
4. 操作系统中磁盘块标记
1.2 布隆过滤器
1.2.1 布隆过滤器提出
我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?
1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
3. 将哈希与位图结合,即布隆过滤器
1.2.2布隆过滤器概念
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
1.2.3 布隆过滤器的插入
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; } }; template<size_t N, size_t X = 5, class K = string, class HashFunc1 = BKDRHash, class HashFunc2 = APHash, class HashFunc3 = DJBHash> class BloomFilter { public: void Set(const K& key) { size_t len = X*N; size_t index1 = HashFunc1()(key) % len; size_t index2 = HashFunc2()(key) % len; size_t index3 = HashFunc3()(key) % len; /* cout << index1 << endl; cout << index2 << endl; cout << index3 << endl<<endl;*/ _bs.set(index1); _bs.set(index2); _bs.set(index3); } bool Test(const K& key) { size_t len = X*N; size_t index1 = HashFunc1()(key) % len; if (_bs.test(index1) == false) return false; size_t index2 = HashFunc2()(key) % len; if (_bs.test(index2) == false) return false; size_t index3 = HashFunc3()(key) % len; if (_bs.test(index3) == false) return false; return true; // 存在误判的 } // 不支持删除,删除可能会影响其他值。 void Reset(const K& key); private: bitset<X*N> _bs; };1.2.4 布隆过滤器的查找
布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特
位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。
注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。
比如:在布隆过滤器中查找"alibaba"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其他元素的比特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的。
1.2.5 布隆过滤器删除
布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。
缺陷:
1. 无法确认元素是否真正在布隆过滤器中
2. 存在计数回绕
1.2.6 布隆过滤器优点
1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
2. 哈希函数相互之间没有关系,方便硬件并行运算
3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
1.2.7 布隆过滤器缺陷
1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
2. 不能获取元素本身
3. 一般情况下不能从布隆过滤器中删除元素
4. 如果采用计数方式删除,可能会存在计数回绕问题
2. 海量数据处理类题目解析
2.1 哈希切割
给一个超过100G大小的log fifile, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
2.2 位图应用
1. 给定100亿个整数,设计算法找到只出现一次的整数?
分析:使用两个比特位,标识3种状态。出现0次:00 出现1次:01 出现2次及以上:10
2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
注:放在位图找交集的意义是先去重。
位图的优点:
1.节省空间
2.处理快速
缺点:只能针对整型映射
2.3 布隆过滤器
1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出
精确算法和近似算法
2. 如何扩展BloomFilter使得它支持删除元素的操作
布隆过滤器映射的位数越多,冲突的概率越小
布隆过滤器的底层就是一个位图
布隆过滤器支持删除,但删除会影响其他位值,因此会付出很大的代价。删除的方式是计数。
因为涉及到附用unordered_set、unordered_map,所以这里给出完整的源码。
3.1位图模拟实现源码:
bitset:
位图1:
#pragma once #include <vector> //位图1 namespace my { // N个比特位的位图 10 16 template<size_t N> class bitset { public: bitset() { // +1保证足够比特位,最多浪费8个 _bits.resize(N / 8 + 1, 0); } //x映射的位标记成1 void set(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; _bits[i] |= (1 << j); } void reset(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; //! && || //~ & | _bits[i] &= (~(1 << j)); } bool test(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; return _bits[i] & (1 << j); } private: std::vector<char> _bits; //vector<int> _bits; }; template<size_t N> class two_bitset { public: void set(size_t x) { int in1 = _bs1.test(x); int in2 = _bs2.test(x); if (in1 == 0 && in2 == 0) { _bs2.set(x); } else if (in1 == 0 && in2 == 1) { _bs1.set(x); _bs2.reset(x); } } bool is_once(size_t x) { return _bs1.test(x) == 0 && _bs2.test(x) == 1; } private: bitset<N> _bs1; bitset<N> _bs2; }; }位图2:
#pragma once #include <vector> //位图2 namespace my { // N个比特位的位图 10 16 template<size_t N> class bitset { public: bitset() { // +1保证足够比特位,最多浪费8个 _bits.resize(N / 8 + 1, 0); } //x映射的位标记成1 void set(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; _bits[i] |= (1 << j); } void reset(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; //! && || //~ & | _bits[i] &= (~(1 << j)); } bool test(size_t x) { // x映射的比特位在第几个char对象 size_t i = x / 8; // x在char第几个比特位 size_t j = x % 8; return _bits[i] & (1 << j); } private: std::vector<char> _bits; //vector<int> _bits; }; template<size_t N> class two_bitset { public: void set(size_t x) { int in1 = _bs1.test(x); int in2 = _bs2.test(x); //00 if (in1 == 0 && in2 == 0) { _bs2.set(x); //01 } //01 else if (in1 == 0 && in2 == 1) { _bs1.set(x); //10 _bs2.reset(x); } //10 else if (in1 == 1 && in2 == 0) { _bs2.set(x); //11 } } bool is_once(size_t x) { return _bs1.test(x) == 0 && _bs2.test(x) == 1; } private: bitset<N> _bs1; bitset<N> _bs2; }; }3.2 布隆过滤器模拟实现源码:
BloomFilter:
3.3 附用的unordered_set、unordered_map、OpenHash:
unordered_set:
#include"OpenHT.h" namespace my { template<class K, class HashFunc = DefaultHash<K>> class unordered_set { //内部类 struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; public: typedef typename Bucket::HashTable<K, K, SetKeyOfT, HashFunc>::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } //bool insert(const K& key) pair<iterator, bool> insert(const K& key) { return _ht.Insert(key); } iterator find(const K& key) { return _ht.Find(key); } bool erase(const K& key) { return _ht.Erase(key); } private: Bucket::HashTable<K, K, SetKeyOfT,HashFunc> _ht; }; }unordered_map:
#include"OpenHT.h" namespace my { template<class K, class V, class HashFunc = DefaultHash<K>> class unordered_map { //内部类 struct MapKeyOfT { const K& operator()(const pair<K,V>& kv) { return kv.first; } }; public: //定义map的迭代器 typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> ::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } //bool insert(const pair<K, V>& kv) pair<iterator,bool> insert(const pair<K, V>& kv) { return _ht.Insert(kv); } iterator find(const K& key) { return _ht.Find(key); } bool erase(const K& key) { return _ht.Erase(key); } V& operator[](const K& key) { //pair<iterator, bool> ret = insert(make_pair(key, V())); auto ret = insert(make_pair(key, V())); return ret.first->second; //first是迭代器,而map里面访问second要用->,其实这里有两个->,但为了可读性 } private: Bucket::HashTable<K, pair<K, V>, MapKeyOfT,HashFunc> _ht; }; }OpenHash:
#pragma once #include<iostream> #include<string> #include<vector> #include<map> #include<set> #include<unordered_map> #include<unordered_set> using namespace std; //仿函数 template<class K> struct DefaultHash //1.普通类直接强转 { size_t operator()(const K& key) { return(size_t)key; //支持取模,强转为整数 } }; template<> struct DefaultHash<string> //String类特化 { size_t operator()(const string& key) { //4.BKDR法 size_t hash = 0; for (auto ch : key) { hash = hash * 131 + ch; } return hash; } }; namespace Bucket { template<class T> struct HashNode { T _data; HashNode<T>* _next; HashNode(const T& data) :_data(data) , _next(nullptr) {} }; template<class K, class T, class KeyOfT, class HashFunc> class HashTable; template<class K, class T, class KeyOfT, class HashFunc> class __HTIterator //单向迭代器 { typedef HashNode<T> Node; typedef __HTIterator<K, T, KeyOfT, HashFunc> Self; public: Node* _node; HashTable<K, T, KeyOfT, HashFunc>* _pht; __HTIterator(){} //默认哈希迭代器构造函数 __HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht) :_node(node) ,_pht(pht) {} Self& operator++() { if (_node->_next) { _node = _node->_next; } else { KeyOfT kot; HashFunc hf; size_t hashi = hf(kot(_node->_data))%_pht->_tables.size(); ++hashi; //找下一个不为空的桶 for (; hashi < _pht->_tables.size(); ++hashi) { if (_pht->_tables[hashi]) { _node = _pht->_tables[hashi]; break; } } // 没有找到不为空的桶,用nullptr去做end标识 if (hashi == _pht->_tables.size()) { _node = nullptr; } } return *this; } T& operator*() { return _node->_data; } T* operator->() { return &_node->_data; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; } }; //unordered_set--->HashTable<K, K, SetKeyOfT> _ht; //unordered_map--->HashTable<K, pair<K, V>, MapKeyOfT> _ht; template<class K, class T, class KeyOfT, class HashFunc> class HashTable { template<class K, class T, class KeyOfT, class HashFunc> friend class __HTIterator; typedef HashNode<T> Node; public: typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator; iterator begin() { for (size_t i = 0; i < _tables.size(); ++i) { Node* cur = _tables[i]; if (cur) { return iterator(cur, this); } } return end(); } iterator end() { return iterator(nullptr, this); } //手动实现Node的析构函数 ~HashTable() { for (size_t i = 0; i < _tables.size(); ++i) { Node* cur = _tables[i]; while (cur) { Node* next = cur->_next; delete cur; cur = next; } _tables[i] = nullptr; } } /// HashTable() = default; //强制生成默认构造函数 链表桶的深拷贝 //HashTable(const HashTable<K, V> ht) //{ // int sz = ht._tables.size(); // for (int i = 0; i < sz; i++) // { // Node* cur = ht._tables[i]; // while (cur) // { // this->Insert(cur->_kv); // cur = cur->_next; // } // } // _n = ht._n; //} 链表桶的赋值重载 //HashTable<K, V>& operator=(HashTable<K, V> ht) //{ // //现代写法 // ht._tablle.swap(_tables); // _n = ht._n; //} /// size_t GetNextPrime(size_t prime) { const int PRIMECOUNT = 28; static const size_t primeList[PRIMECOUNT] = { 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; // 获取比prime大那一个素数 size_t i = 0; for (; i < PRIMECOUNT; ++i) { if (primeList[i] > prime) return primeList[i]; } return primeList[i]; } //bool Insert(const T& data) pair<iterator,bool> Insert(const T& data) { HashFunc hf; KeyOfT kot; //if (Find(kot(data))) //使用仿函数,不知道data是set还是map //if (Find(kot(data))!=end()) //使用仿函数,不知道data是set还是map iterator pos = Find(kot(data)); if(pos!=end()) { //return false; return make_pair(pos,false); } //负载因子(这里设置为1) 扩容 if (_tables.size() == _n) { //写法2:扩容 //size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2; //写法3: size_t newSize = GetNextPrime(_tables.size()); if (newSize != _tables.size()) { vector<Node*> newTable; newTable.resize(newSize, nullptr); for (size_t i = 0; i < _tables.size(); ++i) { Node* cur = _tables[i]; while (cur) { size_t hashi = hf(kot(cur->_data)) % newSize; //hf转换为整型,kot()取k cur->_next = newTable[hashi]; newTable[hashi] = cur; cur = cur->_next; } _tables[i] = nullptr; } newTable.swap(_tables); } } size_t hashi = hf(kot(data)); //两层仿函数调用 hashi %= _tables.size(); //头插到对应的桶即可 Node* newnode = new Node(data); newnode->_next = _tables[hashi]; _tables[hashi] = newnode; ++_n; //return true; return make_pair(iterator(newnode,this),false); } //Node* Find(const K& key) iterator Find(const K& key) { if (_tables.size() == 0) { //return nullptr; return iterator(nullptr,this); } KeyOfT kot; //写法1:有名对象 HashFunc hf; size_t hashi = hf(key); //写法2:匿名对象 //size_t hashi = HashFunc()(key); hashi %= _tables.size(); Node* cur = _tables[hashi]; while (cur) { if (kot(cur->_data) == key) { //return cur; return iterator(cur, this); } cur = cur->_next; } //return nullptr; //效率高,是因为直接返回了指针 return iterator(nullptr, this); } bool Erase(const K& key) { if (_tables.size() == 0) { return false; } KeyOfT kot; //写法1:有名对象 HashFunc hf; size_t hashi = hf(key); hashi %= _tables.size(); Node* cur = _tables[hashi]; Node* prev = nullptr; while (cur) { if (kot(cur->_data) == key) { if (prev == nullptr) { _tables[hashi] = cur->_next; } else { prev->_next = cur->_next; } delete cur; return true; } cur = cur->_next; } return false; } private: //指针数组 //没有使用list,是因为list是双向链表,成本大 vector<list<>> _tables; 但不用手动写析构函数 vector<Node*> _tables; //开散列采用挂起,如果新表扩容,那么当旧表释放,vector会将自己的释放, //但是挂在vector的结点Node* 不会自动释放,因为Node* 是内置类型,需要手动释放。 size_t _n = 0; }; }3.4 test.cpp:
#include"OpenHT.h" #include"UnorderedSet.h" #include"UnorderedMap.h" #include"BitSet.h" #include"BloomFilter.h" // //1.库中的UnorderedSet、UnorderedMap测试 void test_std_UnorderedSet() { std::unordered_set<int> s; s.insert(9); s.insert(7); s.insert(2); s.insert(1); s.insert(6); s.insert(5); //1.迭代器遍历 //unordered_set<int>::iterator it = s.begin(); //auto it = s.begin(); //写法2: std::unordered_set<int>::iterator it; it = s.begin(); while (it != s.end()) { cout << *it << " "; ++it; } cout << endl; //2.范围for遍历 for (auto e : s) { cout << e << " "; } cout << endl; } void test_std_UnorderedMap() { std::unordered_map<string, string> dict; dict.insert(make_pair("sort", "排序")); dict.insert(make_pair("left", "左边")); dict.insert(make_pair("left", "剩余")); dict["string"]; dict["left"] = "剩余"; dict["string"] = "字符串"; //迭代器遍历 //std::unordered_map<string, string>::iterator it = dict.begin(); auto it = dict.begin(); while (it != dict.end()) { cout << it->first << " " << it->second << endl; ++it; } cout << endl; //范围for遍历 for (auto& kv : dict) { cout << kv.first << " " << kv.second << endl; } } // //2.模拟实现UnorderedSet、UnorderedMap测试 void test_my_UnorderedSet() { my::unordered_set<int> s; s.insert(9); s.insert(7); s.insert(2); s.insert(1); s.insert(6); s.insert(5); //1.迭代器遍历 //my::unordered_set<int>::iterator it = s.begin(); //auto it = s.begin(); //写法2: my::unordered_set<int>::iterator it; it = s.begin(); while (it != s.end()) { cout << *it << " "; ++it; } cout << endl; //2.范围for遍历 for (auto e : s) { cout << e << " "; } cout << endl; } void test_my_UnorderedMap() { my::unordered_map<string, string> dict; dict.insert(make_pair("sort", "排序")); dict.insert(make_pair("left", "左边")); dict.insert(make_pair("left", "剩余")); dict["string"]; dict["left"] = "剩余"; dict["string"] = "字符串"; //迭代器遍历 //my::unordered_map<string, string>::iterator it = dict.begin(); auto it = dict.begin(); while (it != dict.end()) { cout << it->first << " " << it->second << endl; ++it; } cout << endl; //范围for遍历 for (auto& kv : dict) { cout << kv.first << " " << kv.second << endl; } } // //3.模拟实现bitset测试 void test_my_bit_set() { my::bitset<100> bs; bs.set(18); bs.set(19); cout << bs.test(18) << endl; cout << bs.test(19) << endl; cout << bs.test(20) << endl; bs.reset(18); bs.reset(18); cout << bs.test(18) << endl; cout << bs.test(19) << endl; cout << bs.test(20) << endl; //my::bitset<INT_MAX> bigBS; my::bitset<0xFFFFFFFF> bigBS; //my::bitset<-1> bigBS; //bit_set<2^32-1> bigBS; // pow } //4.模拟实现two_bitset void test_my_two_bitset() { int a[] = { 4, 3, 2, 4, 5, 2, 2, 4, 7, 8, 9, 2, 1,3,7 }; my::two_bitset<10> tbs; for (auto e : a) { tbs.set(e); } for (size_t i = 0; i < 10; ++i) { if (tbs.is_once(i)) { cout << i << " "; } } cout << endl; } //5.模拟实现BloomFilter测试 void test_my_BloomFilter1() { //插入10个数据 //写法1:43是计算的值(具体参见文档) //my::BloomFilter<string, 43, my::BKDRHash, my::APHash, my::DJBHash> bf; //写法2:43是计算的值(具体参见文档),因为模板给了缺省值,因此这里只传第一个数据类型即可 my::BloomFilter<43> bf; string a[] = { "铭记历史","勿忘国耻","振兴中华","吾辈自强","1931.09.18","九*一八事变","2022.09.18"}; for (auto e : a) { cout << e <<" "; } cout << endl; for (auto& e : a) { bf.Set(e); } for (auto& e : a) { cout << bf.Test(e) << endl; } cout << bf.Test("缅怀先烈") << endl; cout << bf.Test("吾辈自强") << endl; cout << bf.Test("砥砺前行") << endl; } //6.模拟实现布隆过滤器测试对比误判率 void test_my_BloomFilter2() { srand(time(0)); //给不同的值,观察误判率 //一般来说,空间越大,冲突率越小 const size_t N = 10000; /*const size_t N = 60000; const size_t N = 100000;*/ /*my::BloomFilter<5 * N> bf; my::BloomFilter<6 * N> bf;*/ my::BloomFilter<8 * N> bf; std::vector<std::string> v1; std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html"; for (size_t i = 0; i < N; ++i) { v1.push_back(url + std::to_string(1234 + i)); } for (auto& str : v1) { bf.Set(str); } //打印判断的key的bool返回值(1或0) /*for (auto& str : v1) { cout << bf.Test(str) << endl; } cout << endl << endl;*/ //给一个相似的字符串 std::vector<std::string> v2; for (size_t i = 0; i < N; ++i) { std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html"; url += std::to_string(999999 + i); v2.push_back(url); } //判断并统计。如果在,就是误判 size_t n2 = 0; for (auto& str : v2) { if (bf.Test(str)) { ++n2; } } cout << "相似字符串误判率:" << (double)n2 / (double)N << endl; std::vector<std::string> v3; for (size_t i = 0; i < N; ++i) { string url = "zhihu.com"; url += std::to_string(rand()); v3.push_back(url); } size_t n3 = 0; for (auto& str : v3) { if (bf.Test(str)) { ++n3; } } cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl; } int main() { //test_std_UnorderedSet(); //test_std_UnorderedMap(); //test_my_UnorderedSet(); //test_my_UnorderedMap(); //test_my_bit_set(); //test_my_two_bitset(); //test_my_BloomFilter1(); test_my_BloomFilter2(); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。