位图|布隆过滤器模拟实现|STL源码剖析系列|手撕STL
今天博主给大家带来位图和布隆过滤器的模拟实现。
前言
那么这里博主先安利一下一些干货满满的专栏啦!
这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482
这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html
STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482
位图和布隆过滤器
位图
位图(Bitmap)通常指的是使用位(bit)作为最小单位存储和处理数据的数据结构或技术。位图可以用来表示一组二进制标志或位状态,并且可以有效地压缩存储大量布尔信息。
位图最常见的用途之一是表示集合或标志的状态。例如,可以使用位图来表示一个包含多个元素的集合,其中每个元素对应位图中的一个位。如果位的值为1,则表示该元素在集合中;如果位的值为0,则表示该元素不在集合中。
在C语言中,可以使用无符号整数类型(如unsigned int
、unsigned long
)或数组来实现位图。
布隆过滤器
布隆过滤器(Bloom Filter)是一种用于高效判断一个元素是否属于一个集合的概率型数据结构。它基于位图(Bitmap)的概念,但使用了多个哈希函数来实现更高的查找效率。
布隆过滤器由一个位数组和多个哈希函数组成。初始时,所有位数组的值都被设置为0。当要向布隆过滤器中插入一个元素时,该元素经过多个哈希函数的计算,得到多个哈希值。然后将对应的位数组位置设置为1。当需要判断一个元素是否在集合中时,同样经过多个哈希函数的计算,检查对应的位数组位置是否都为1。如果有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则表示该元素可能在集合中(存在误判的概率)。
布隆过滤器的主要优势是其高效的插入和查询操作。它的时间复杂度是O(k),其中k是哈希函数的数量,通常是一个较小的常数。布隆过滤器的空间复杂度也相对较低,只受到位数组的大小和哈希函数数量的影响。
然而,布隆过滤器也有一些限制。首先,存在一定的误判率,即在判断元素是否在集合中时,有一定的概率会出现错误的判断。其次,无法删除已插入的元素,因为删除操作会影响其他元素的判断结果。因此,布隆过滤器适用于对查询速度要求较高、可以容忍一定误判率的场景,如缓存、防止重复操作等。
需要根据具体的应用场景和数据特点来选择使用布隆过滤器,并在设计时注意误判率的控制和容量估算,以达到最佳效果。
BitSet.h
#pragma once
#include<vector>
#include<iostream>
using namespace std;
//位图特点
//1.快、节省空间
//2.相对局限,只能映射处理整型
//用char -- 一个char位置存8位
//怎么找位置,比如20
//20/8=2表示放在第几个char上
//20%8=4表示放在这个char的第几个位置
namespace yfc
{
template<size_t N>
class bit_set
{
public:
bit_set()
{
_bits.resize(N / 8 + 1, 0);//+1可以保证空间一定够
}
void set(size_t x)
{
//把x的位置设置成1
size_t i = x / 8;
size_t j = x % 8;
//怎么把_bit[i]的第j位弄成1呢
//用一个或运算!
_bits[i] |= (1 << j);
}
void reset(size_t x)
{
//把x的位置设置成0
size_t i = x / 8;
size_t j = x % 8;
_bits[i] &= ~(1 << j);
}
bool test(size_t x)
{
//看这一位是0还是1
size_t i = x / 8;
size_t j = x % 8;
return _bits[i] & (1 << j);
}
private:
vector<char> _bits;
};
void test_bit_set1()
{
bit_set<100>bs1;
bs1.set(8);
bs1.set(9);
bs1.set(20);
cout << bs1.test(8) << endl;
cout << bs1.test(9) << endl;
cout << bs1.test(20) << endl;
bs1.reset(8);
bs1.reset(9);
bs1.reset(20);
cout << bs1.test(8) << endl;
cout << bs1.test(9) << endl;
cout << bs1.test(20) << endl;
}
void test_bit_set2()
{
//这三种写法都可以
bit_set<-1>bs1;//-1的写法是最好的 -- -1对应的size_t就是全1
#if 0
bit_set<0xffffffff>bs2;
bit_set < 1024 * 1024 * 1024 * 4 - 1> bs3;
#endif
//我们打开看任务管理器 -- 是可以看到是512MB左右的
}
//面试题2
//我们用两个位图就行 -- 两个位图对应位置一起表示状态
template<size_t N>
class twobitset
{
private:
bit_set<N>_bs1;
bit_set<N>_bs2;
public:
void set(size_t x)
{
//要先判断一下
bool inSet1 = _bs1.test(x);
bool inSet2 = _bs2.test(x);
if (inSet1 == false && inSet2 == false)
{
//00->01
_bs2.set(x);
}
else if (inSet1 == false && inSet2 == true)
{
//01->10
_bs1.set(x);
_bs2.reset(x);
}
}
void print_once_num()
{
for (size_t i = 0; i < N; i++)
{
if (_bs1.test(i) == false && _bs2.test(i) == true)
{
cout << i << " ";
}
}
cout << endl;
}
};
void test_bit_set3()
{
int a[] = { 1,2,3,4,5,6,7,8,9,10,12,10,9,8,6,5,3,2,1 };
twobitset<100>bs;
for (auto e : a)
{
bs.set(e);
}
bs.print_once_num();
}
//面试题3
//也是用两个位图
//第一个是文件1的映射
//第二个是文件2的映射
//映射位都是1的值就是交集
//面试题4
//其实和2是一样的,00/01/10/11就行
}
BloomFilter.h
#pragma once
//布隆过滤器
#include"BitSet.h"
#include<algorithm>
#include<string>
using namespace std;
namespace yfc
{
template<class K = string>
struct HashBKDR
{
size_t operator()(const K& key)
{
size_t val = 0;
for (auto ch : key)
{
val *= 131;
val += ch;
}
return val;
}
};
template<class K = string>
struct HashAP
{
size_t operator()(const K& key)
{
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ key[i] ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ key[i] ^ (hash >> 5)));
}
}
return hash;
}
};
template<class K = string>
struct HashDJB
{
size_t operator()(const K& key)
{
size_t hash = 5381;
for (auto ch : key)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
template<size_t N, class K = string,
class Hash1 = HashBKDR<string>,
class Hash2 = HashAP<string>,
class Hash3 = HashDJB<string>>
class BloomFilter
{
private:
const static size_t _ratio = 5;
bit_set<_ratio* N> _bits;
//如果使用std::bitset
//考虑放到堆上new一个
//因为std::bit有个隐藏的bug会把栈撑爆
public:
void set(const K& key)
{
size_t hash1 = Hash1()(key) % (_ratio * N);
_bits.set(hash1);
size_t hash2 = Hash2()(key) % (_ratio * N);
_bits.set(hash2);
size_t hash3 = Hash3()(key) % (_ratio * N);
_bits.set(hash3);
}
bool test(const K& key)
{
size_t hash1 = Hash1()(key) % (_ratio * N);
if (!_bits.test(hash1))return false;
size_t hash2 = Hash2()(key) % (_ratio * N);
if (!_bits.test(hash2))return false;
size_t hash3 = Hash3()(key) % (_ratio * N);
if (!_bits.test(hash3))return false;
return true;//可能存在误判 -- 上面的不在是准确的
}
};
void testBloomFilter1()
{
BloomFilter<10>bf;
string arr[] = { "苹果","西瓜","阿里","美团","苹果","字节","西瓜","苹果","香蕉","苹果","腾讯" };
for (auto& str : arr)
{
bf.set(str);
}
for (auto& str : arr)
{
cout << bf.test(str) << endl;
}
}
//测误判率的性能测试
void TestBloomFilter2()
{
srand(time(0));
const size_t N = 100000;
BloomFilter<N> bf;
cout << sizeof(bf) << endl;
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);
}
// 相似
std::vector<std::string> v2;
for (size_t i = 0; i < N; ++i)
{
std::string url = "http://www.cnblogs.com/-clq/archive/2021/05/31/2528153.html";
url += std::to_string(rand() + 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() + i);
v3.push_back(url);
}
size_t n3 = 0;
for (auto& str : v3)
{
if (bf.test(str))
{
++n3;
}
}
cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
}
}
位图|布隆过滤器模拟实现|STL源码剖析系列|手撕STL的更多相关文章
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- 【转载】STL"源码"剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- (原创滴~)STL源码剖析读书总结1——GP和内存管理
读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...
- 《STL源码剖析》环境配置
首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- STL"源码"剖析
STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...
- 《STL源码剖析》相关面试题总结
原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
随机推荐
- POJ - 3087:Shuffle'm Up (字符串模拟)
一.内容 题意:给定2个字符串s1,s2,将2个字符串进行重组成S,规则是S2最下面拿一个,S1最下面拿1个,直到所有块都用完. 二.思路 用map记录下S串结果,若以前访问过这个串代表不可能有结果直 ...
- 【每日一题】12.Running Median (对顶堆)
补题链接:Here 题意:动态的维护中位数的问题,依次读入一个整数,每当总个数为奇数时输出此时序列的中位数 使用对顶堆的在线做法. 为了实时找到中位数,我们可以建议两个二叉堆:一个小根堆.一个大根堆. ...
- vue Promise的使用
一.Promise是什么? Promise是异步编程的一种解决方案. 二.那什么时候我们会来处理异步事件呢? 1. 一种很常见的场景应该就是网络请求了. 我们封装一个网络请求的函数,因为不能立即拿到结 ...
- Redhat5 和Redhat6安装oracle11g
oralce安装本人认为最麻烦的就是oracle包的版本和oracle的依赖的包的问题,这个做不好后期安装过程就会出现很多诡异的问题,这里总结一下Redhat5 和Redhat6安装oracle11g ...
- VScode快捷键和设置
https://blog.csdn.net/qq_35206261/article/details/85207428
- vue如何实现v-model
- @Configuration 注解使用及源码解析
本文为博主原创,转载请注明出处: @Configuration 注解对我们来说并不陌生,以javaConfig的方式定义spring IOC容器的配置类使用的就是这个@Configuration. s ...
- MAUI使用Masa blazor组件库
上一篇(点击阅读)我们实现了UI在Web端(Blazor Server/Wasm)和客户端(Windows/macOS/Android/iOS)共享,这篇我加上 Masa Blazor组件库的引用,并 ...
- ASIC 功能验证VTB
目标 设计流程 验证设计文档和RTL code之间的关系 RTL code(DUT) - 可以当作是一个黑盒,DUT内部是完全不可见的 白盒验证 - DUT内部RTL完全可见 灰盒验证 - DUT内部 ...
- WebApi允许跨域
services.AddCors(options => { options.AddPolicy("abc", builder => { //App:CorsOrigin ...