今天博主给大家带来位图和布隆过滤器的模拟实现。


前言

那么这里博主先安利一下一些干货满满的专栏啦!

手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。

STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482


位图和布隆过滤器

位图

位图(Bitmap)通常指的是使用位(bit)作为最小单位存储和处理数据的数据结构或技术。位图可以用来表示一组二进制标志或位状态,并且可以有效地压缩存储大量布尔信息。

位图最常见的用途之一是表示集合或标志的状态。例如,可以使用位图来表示一个包含多个元素的集合,其中每个元素对应位图中的一个位。如果位的值为1,则表示该元素在集合中;如果位的值为0,则表示该元素不在集合中。

在C语言中,可以使用无符号整数类型(如unsigned intunsigned 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的更多相关文章

  1. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  2. 【转载】STL"源码"剖析-重点知识总结

    原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...

  3. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  4. (原创滴~)STL源码剖析读书总结1——GP和内存管理

    读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...

  5. 《STL源码剖析》环境配置

    首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...

  6. STL源码剖析读书笔记之vector

    STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...

  7. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  8. STL"源码"剖析

    STL"源码"剖析-重点知识总结   STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...

  9. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

  10. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

随机推荐

  1. 阿里云 X 森马 AIGC T恤设计大赛开启! 穿什么由你定,赢Airpods,作品定制联名T恤

    "关于宇宙,我所知道的最富诗意的事实之一就是, 我们身体中的每一个原子都曾经存在于某一颗爆发的恒星里. 组成你左手的原子和组成你右手的原子 很有可能来自不同的恒星, 而我们都是恒星的孩子, ...

  2. mixin混合

    多个组件有相同的逻辑,抽离出来 mixin并不是完美的解决方案,会有一些问题 vue3提出composition api旨在解决这些问题

  3. sipp3.6带媒体测试方案

    概述 SIP压测工具sipp,免费,开源,功能足够强大,配置灵活,优点多. 本文档介绍sipp工具如何带媒体测试,并介绍如何制作可用的媒体文件(G729和PCMA). 环境 centos7.9 fre ...

  4. CF1656F Parametric MST 题解

    为了便于解题,先对 \(a\) 数组从小到大进行排序. 首先,根据定义可以得出总价值的表达式: \[\begin{aligned} W&=\sum\limits_{(u,v)\in E}[a_ ...

  5. idea开发常用快捷键总结

    转载请注明出处: idea提供了很多的快捷键,但在开发过程中并发全用,只是常用部分快捷键,在这里总结一下,总结的不全,有好的快捷键可在评论里补充下,提前谢各位 由于很早之前用的eclipse或spri ...

  6. springboot升级到2.6.x和2.7.x 兼容hystrix

    一.pom.xml需要引入的依赖 二.项目开启熔断器开关 2.1 注解方式 2.2 xml方式 三.依赖类缺失问题 四.版本匹配安全检查问题 五.测试验证 六.结论 一.pom.xml需要引入的依赖 ...

  7. Cortex M3 CORE

    Cortex CM3 内核架构 CM3内核主要包含几个部分:取指(Fetch)\指令译码(Decoder/DEC)\执行(EXEC)\ALU 内存取数通过load & store指令,就是通过 ...

  8. 02-VS调试以及Qt基本使用

    VS调试以及Qt基本使用 1.汇编语言 1.1 VS中C语言嵌套汇编代码(了解) #include <stdio.h> int main() { //定义整型变量a, b, c int a ...

  9. Python Code_05位运算

    coding:utf-8 author : 写bug的盼盼 development time : 2021/8/28 7:16 print(4&8)#非1即0 print(4|2)#同0即0, ...

  10. [转帖]shell编程:变量的数值计算实践(五)

    https://www.cnblogs.com/luoahong/articles/9224495.html 算术运算符 变量的数值(整数)计算   1)(())用法:(此方法很常用)** 范例:sh ...