1. #include<time.h>
  2. #include<string>
  3. #include<vector>
  4. #include<iostream>
  5. using std::cout;
  6. using std::endl;
  7. using std::string;
  8.  
  9. namespace test
  10. {
  11. /**位图概念
  12. * 所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
  13. *
  14. * 位图的应用
  15. * 1. 快速查找某个数据是否在一个集合中
  16. * 2. 排序 + 去重
  17. * 3. 求两个集合的交集、并集等
  18. * 4. 操作系统中磁盘块标记
  19. * 如果能够映射到位图,则会使等价的数据量大大减小,使其能够进入内存
  20. * 位图效率极高,时间复杂度O(1),节省内存
  21. *
  22. * 缺点:只能映射整型
  23. *
  24. */
  25.  
  26. template<size_t N> // 非类型模板参数 -- N一般给最大值
  27. class bitset //位图可以叫做bitmap好点,不过stl叫做bitset
  28. {
  29. /** 位图
  30. *
  31. * 位图在C++标准库std中
  32. * https://legacy.cplusplus.com/reference/bitset/bitset/
  33. *
  34. * 位图功能:
  35. * $ 能够操作比特位,在某些场景下能使消耗空间大大 减小,足以容纳进内存,快速执行
  36. *
  37. *
  38. * 需要实现的功能:
  39. * 1.接收数用于开辟多大空间 -- 非类型模板参数 -- 根据不同情况传不同的值,位图还有很多功能,如果计算无符号整型需要传整型最大值
  40. * 2.能够对位图某一比特位 置零 -- reset
  41. * 3.能够对位图某一比特位 置1 -- set
  42. * 4.能够得知某一比特位是0还是1 -- return bool ret
  43. *
  44. * 成员
  45. * 1.成员:char数组 - vectot
  46. *
  47. * 位图调试,在监视窗口中获取原始视图的指针,然后从数组首地址开始看内存,内存是从右往左,从上往下看 ,两个字母为1个字节,字节内按二进制写法
  48. *
  49. */
  50.  
  51. private:
  52. std::vector<char> _bits; //不允许访问,因为实现位图必须通过特殊操作
  53.  
  54. public:
  55. bitset()
  56. {
  57. //1.求所需要的字节数,需要至少有N个bit位,而8bit一个字节,所以至少需要N/8个字节,由于会截断,故需要+1
  58. //2.必须全部置成0,或者1(如果逻辑全部取反的话)
  59. _bits.resize(N / 8 + 1, 0);
  60. }
  61.  
  62. void set(size_t x) //置1
  63. {
  64. int i = x / 8; //确定下标
  65. int j = x % 8; //确定该字节内的第几位 -- 用来左移,定位到第j比特位
  66. //置1 对左移j位 进行或运算
  67. _bits[i] |= 1 << j;
  68.  
  69. }
  70.  
  71. void reset(size_t x) //置0
  72. {
  73.  
  74. int i = x / 8; //确定下标
  75. int j = x % 8; //确定该字节内的第几位 -- 用来左移,定位到第j比特位
  76. //置0: 对取反后的左移j位的1 进行与运算
  77. _bits[i] &= ~(1 << j);
  78. }
  79.  
  80. bool test(size_t x) //返回x所在的位是0或1 //标准库就叫做test
  81. {
  82. int i = x / 8; //确定下标
  83. int j = x % 8; //确定该字节内的第几位 -- 用来左移,定位到第j比特位
  84. return _bits[i] & (1 << j);
  85. }
  86.  
  87. //bitset& filp(size_t x = N) //翻转全部bit位或某一位
  88. //{
  89.  
  90. //}
  91.  
  92. };
  93.  
  94. void test_bitset1()
  95. {
  96. test::bitset<100> bs;//测试用
  97. //test::bitset<-1> bs;//无符号整型最大值 -- 和size_t npos = -1 一样 -- 可以看资源管理器,开的内存空间
  98. bs.set(10);
  99. bs.set(11);
  100. bs.set(15);
  101. cout << bs.test(10) << endl;
  102. cout << bs.test(11) << endl;
  103.  
  104. bs.reset(10);
  105. bs.reset(11);
  106. cout << bs.test(10) << endl;
  107. cout << bs.test(11) << endl;
  108. }
  109.  
  110. }
  111.  
  112. namespace test2
  113. {
  114. /** 位图扩展
  115. *
  116. * 位图玩法多种多样,多练习才能驾驭
  117. *
  118. *
  119. */
  120.  
  121. template<size_t N>
  122. class twobitset //开了两个位图的封装
  123. {
  124. public:
  125. void set(size_t x)
  126. {
  127. /** 原理
  128. * 通过双位图,给三种状态 00,01,10;
  129. * 00代表 0次
  130. * 01代表 1次
  131. * 10代表 2次及以上
  132. *
  133. * 个位用bs1控制,十位用bs2控制
  134. * 示例图如:
  135. * _bs1:▭▭▭▭▭▭▭▭▭▭011
  136. * _bs2:▭▭▭▭▭▭▭▭▭▭101
  137. *
  138. */
  139. if (_bs1.test(x) == false && _bs2.test(x) == false)
  140. {
  141. _bs1.set(x);
  142. }
  143. else if (_bs1.test(x) == true && _bs2.test(x) == false)
  144. {
  145. _bs1.reset(x);
  146. _bs2.set(x);
  147. }
  148. }
  149.  
  150. bool test(size_t x)
  151. {
  152. return _bs1.test(x) == true && _bs2.test(x) == false;//由题,只出现1次返回真
  153. }
  154.  
  155. private:
  156. test::bitset<N> _bs1; //已经初始化成0了
  157. test::bitset<N> _bs2;
  158. };
  159.  
  160. void test_twobitset1()
  161. {
  162. int a[] = { 3, 45, 53, 32, 32, 43, 3, 2, 5, 2, 32, 55, 5, 53,43,9,8,7,8 };
  163. test2::twobitset<100> bs;
  164. for (auto i : a)
  165. {
  166. bs.set(i);
  167. }
  168. for (auto i : a)
  169. {
  170. if (bs.test(i))
  171. {
  172. cout << i << " ";
  173. }
  174. }
  175.  
  176. }
  177.  
  178. }
  179.  
  180. //---------------------------------------------------------------------------------------------------------
  181.  
  182. //布隆过滤器 --- 解决:某样东西一定不存在或者可能存在
  183. /**
  184. * 布隆过滤器提出
  185. * 我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉
  186. * 那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用
  187. * 户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那
  188. * 些已经存在的记录。 如何快速查找呢?
  189. * 1. 用哈希表存储用户记录,缺点:浪费空间
  190. * 2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理
  191. * 了。
  192. * 3. 将哈希与位图结合,即布隆过滤器
  193. * 解决:将所有广告都映射到布隆过滤器中,然后在匹配和比对
  194. *
  195. * 对于字符串的组合有265^n种,n为长度,n如果很大,那将会比无符号整型最大值大很多,所以一般的数据结构是不能支持的
  196. * 单纯使用位图,也一定会存在冲突(有重复的哈希值)问题,位图是不允许冲突的,哈希的话空间开销太大
  197. *
  198. * 布隆过滤器就是在位图和哈希的基础上结合而成,对一个数据计算多个哈希映射到位图上,即一个数据占了位图的多个bit
  199. * 主要目的是降低冲突的概率,允许误判,不是根绝冲突的发生
  200. *
  201. * 布隆过滤器不存在一定是正确的,存在可能是误判 -- 冲突
  202. *
  203. * 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,
  204. * 可以用来告诉你 “某样东西一定不存在或者可能存在”。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。
  205. *
  206. * 显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。
  207. * 哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低,花费空间也会增多;但是如果太少的话,那我们的误报率会变高。
  208. * 哈希函数个数代表一个值映射几个位
  209. *
  210. * 布隆过滤器不方便删除,可以重建来达到修改目的 -- 一种支持删除的方法:用多个bit位来计数,同理开销会增大,看情况使用
  211. *
  212. * 查找时间复杂度O(1)
  213. *
  214. */
  215.  
  216. /** 布隆过滤器优点
  217. *
  218. * 1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  219. * 2. 哈希函数相互之间没有关系,方便硬件并行运算
  220. * 3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  221. * 4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  222. * 5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  223. * 6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
  224. *
  225. */
  226.  
  227. /**布隆过滤器缺陷
  228. * 1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再
  229. * 建立一个白名单,存储可能会误判的数据)
  230. * 2. 不能获取元素本身
  231. *
  232. *
  233. * 3. 一般情况下不能从布隆过滤器中删除元素
  234. * 4. 如果采用计数方式删除,可能会存在计数回绕问题,丧失低开销优势,还有可能有溢出问题
  235. *
  236. *
  237. * .
  238. */
  239.  
  240. /** 布隆过滤器的使用场景 -- 非整型数据存不存在
  241. * 1.快速响应且容许误判的场景:注册昵称是否存在
  242. *
  243. * 2.手机号注册:如果bloom判断不存在,则直接返回,响应很快.如果在,再去数据库中确认后再返回精确结果 --- 精确且效率高 -- 比纯布隆慢一点点
  244. * 布隆过滤器发挥作用,可以快速过滤掉大量数据,剩下极小部分数据可以方便使用其他数据结构解决
  245. * (很实用)
  246. *
  247. * $如果能容忍误判就可以直接用了
  248. * $如果不能容忍误判则当过滤器用 -- 最后一般都在数据库中查找--信息型数据
  249. * 数据分为信息型,数据型,内容型,文件型.....(不准确)
  250. *
  251. * 最佳实践
  252. * 常见的适用常见有,利用布隆过滤器减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求。
  253. *
  254. */
  255.  
  256. //布隆过滤器变量控制
  257. /**
  258. * k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率
  259. * $ -1 * (ln2)^2 * m = n * lnp
  260. * $ k * n = m * ln2;
  261. *
  262. */
  263.  
  264. namespace BloomFilter
  265. {
  266. struct BKDRHash
  267. {
  268. size_t operator()(const string& s)
  269. {
  270. size_t hash = 0;
  271. for (auto ch : s)
  272. {
  273. hash += ch;
  274. hash *= 31;
  275. }
  276. return hash;
  277. }
  278. };
  279.  
  280. struct APHash
  281. {
  282. size_t operator()(const string& s)
  283. {
  284. size_t hash = 0;
  285. for (size_t i = 0; i<s.size(); i++)
  286. {
  287. size_t ch = s[i];
  288. if ((i & 1) == 0) //偶数
  289. {
  290. hash ^= ((hash << 7) ^ ch ^ (hash >> 3)); //
  291. }
  292. else //奇数
  293. {
  294. hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
  295. }
  296. }
  297. return hash;
  298. }
  299. };
  300.  
  301. struct DJBHash
  302. {
  303. size_t operator()(const string& s)
  304. {
  305. size_t hash = 5381;
  306. for(auto ch:s)
  307. {
  308. hash += (hash << 5) + ch;
  309. }
  310. return hash;
  311. }
  312. };
  313.  
  314. template<size_t N, class K = string, class Hash1 =BKDRHash , class Hash2 =APHash , class Hash3=DJBHash> //可以有多个hash
  315. class bloomfilter
  316. {
  317. private:
  318. //开辟多少空间根据公式k * n = m * ln2; m 为布隆过滤器长度,n 为插入的元素个数,k为哈希个数
  319. /**
  320. * m = k/ln2 * N ;K=3,3/ln2 = 4.32...=4 ;
  321. *
  322. */
  323. static const size_t _X = 6; //数字是相当于每个值使用多少位 //计算出来是4,但感觉4误判率高很多,使用6开销也大很多,得具体验证才知好坏
  324. test::bitset<N * _X> _bs; //_X越大消耗越多
  325.  
  326. public:
  327. void set(const K& key)
  328. {
  329. //Hash1 hash1;具体对象写法:hash1(key)
  330. // 匿名对象写法Hash1()(key);
  331.  
  332. size_t len = N * _X;//长度
  333. size_t hash1 = Hash1()(key) % len;//匿名对象写法
  334. _bs.set(hash1);
  335. size_t hash2 = Hash2()(key) % len;
  336. _bs.set(hash2);
  337. size_t hash3 = Hash3()(key) % len;
  338. _bs.set(hash3);
  339. //cout << hash1 << " " << hash2 << " " << hash3 << " " << endl; //观察哈希值的数据
  340. }
  341.  
  342. bool test(const K& key)
  343. {
  344. size_t len = N * _X;//长度
  345.  
  346. //只要有一个不是1就是不存在,所有都存在才可能存在
  347. size_t hash1 = Hash1()(key) % len;
  348. if (_bs.test(hash1) == false)
  349. {
  350. return false;
  351. }
  352. size_t hash2 = Hash2()(key) % len;
  353. if (_bs.test(hash2) == false)
  354. {
  355. return false;
  356. }
  357. size_t hash3 = Hash3()(key) % len;
  358. if (_bs.test(hash3) == false)
  359. {
  360. return false;
  361. }
  362.  
  363. return true;
  364. }
  365.  
  366. };
  367.  
  368. void test_BloomFilter1()
  369. {
  370. BloomFilter::bloomfilter<100> bs;
  371. bs.set("sort");
  372. bs.set("bloom");
  373. bs.set("hello world hello bit");
  374. bs.set("test");
  375. bs.set("etst");
  376. bs.set("estt");
  377.  
  378. cout << bs.test("sort") << endl;
  379. cout << bs.test("bloom") << endl;
  380. cout << bs.test("hello world hello bit") << endl;
  381. cout << bs.test("test") << endl;
  382. cout << bs.test("etst") << endl;
  383. cout << bs.test("estt") << endl;
  384.  
  385. cout << bs.test("ssort") << endl;
  386. cout << bs.test("tors") << endl;
  387. cout << bs.test("ttes") << endl;
  388. }
  389.  
  390. void test_BloomFilter2()
  391. {
  392. srand((size_t)time(0));
  393. const size_t N = 100000; //切换release,不然很慢
  394. BloomFilter::bloomfilter<N> bf;
  395.  
  396. //基准,用于与下面两个比较
  397. std::vector<std::string> v1;
  398. std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
  399.  
  400. for (size_t i = 0; i < N; ++i)
  401. {
  402. v1.push_back(url + std::to_string(i));
  403. }
  404.  
  405. for (auto& str : v1)
  406. {
  407. bf.set(str);
  408. }
  409. //-----------------------------------------------------------------------------
  410.  
  411. // v2跟v1是相似字符串集,但是不一样
  412. std::vector<std::string> v2;
  413. for (size_t i = 0; i < N; ++i)
  414. {
  415. std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
  416. url += std::to_string(999999 + i);
  417. v2.push_back(url);
  418. }
  419.  
  420. size_t n2 = 0;
  421. for (auto& str : v2)
  422. {
  423. if (bf.test(str))
  424. {
  425. ++n2;
  426. }
  427. }
  428. cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;
  429.  
  430. // 不相似字符串集
  431. std::vector<std::string> v3;
  432. for (size_t i = 0; i < N; ++i)
  433. {
  434. string url = "zhihu.com";
  435. //string url = "https://www.cctalk.com/m/statistics/live/16845432622875";
  436. url += std::to_string(i + rand());
  437. v3.push_back(url);
  438. }
  439.  
  440. size_t n3 = 0;
  441. for (auto& str : v3)
  442. {
  443. if (bf.test(str))
  444. {
  445. ++n3;
  446. }
  447. }
  448. cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
  449. }
  450.  
  451. }

STL-bitset模拟实现的更多相关文章

  1. C++ STL bitset 容器详解

    C++ STL bitset 容器详解 本篇随笔讲解\(C++STL\)中\(bitset\)容器的用法及常见使用技巧. \(bitset\)容器概论 \(bitset\)容器其实就是个\(01\)串 ...

  2. <泛> STL - vector 模拟实现

    今天为大家带来一个模拟STL-vector的模板实现代码. 首先看一下测试结果,之后再为大家呈现设计 测试效果 测试代码 #include<iostream> #include<ve ...

  3. 洛谷 P1739 表达式括号匹配【STL/stack/模拟】

    题目描述 假设一个表达式有英文字母(小写).运算符(+,-,*,/)和左右小(圆)括号构成,以"@"作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返 ...

  4. Codeforces.97D.Robot in Basement(bitset 模拟)

    题目链接 (ozr attack) 考虑怎么暴力,就是先在所有非障碍格子上全放上机器人,然后枚举每一步,枚举每个机器人移动这一步,直到所有机器人都在出口位置.复杂度是\(O(nmk)\)的. 怎么优化 ...

  5. STL::bitset

    bitset: A bitset stores bits.大小通过参数传递,在编译时确定.可变的可参考 vector<bool>. constructor default: integer ...

  6. <泛> STL - stack 模拟实现

    今天,看C++Template的时候看到那人写了一个Stack,于是乎,手痒,自己也写了一个,在拜读了STD文件和C++模板元编程某些小节之后,你们就看到了这篇代码. 经过上述一番经历之后,我重新写了 ...

  7. 【转载】【bitset】C++ STL bitset 使用总结

    C++ bitset类的使用与简介 有些程序要处理二进制位的有序集,每个位可能包含的是0(关)或1(开)的值.位是用来保存一组项或条件的yes/no信息(有时也称标志)的简洁方法.标准库提供了bits ...

  8. C++ STL bitset总结

    基础用法 C++ Reference 神犇博客 余下的就是例题了 [BZOJ3687]简单题 考虑\(DP\),设\(f[i][j]\)表示前\(i\)个元素的算数和为\(j\)的子集个数,有: \[ ...

  9. STL————bitset

    C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间. bitset<> bitset1; //无参构造,长度为 ...

  10. STL string 模拟

    下面的代码来自c++ primer plus第5版第12章,书中代码写的非常好: // string1.h -- fixed and augmented string class definition ...

随机推荐

  1. ES6 Array.fiill()的用法

    简单使用 // arr.fill(value, start, end) // value填充的值 // start填充的起始位置包含 // end填充的结束值,不包含,如果省略这个参数,表示从起始位置 ...

  2. 希尔伯特变换用于解调系统——以解调调频信号为例,FM Demodulation

    What's The Hilbert Transform 简单地说,希尔伯特变换的物理意义为:把信号的所有频率分量的相位推迟90度,这样原信号和变换后信号可以视为一组IQ正交信号,在数字域正交化,可以 ...

  3. Go中字符串处理:fmt.Sprintf与string.Builder的比较

    在Go语言中,我们通常会遇到两种主要的方式来处理和操作字符串:使用fmt.Sprintf函数和string.Builder类型.尽管两者都可以实现字符串的格式化和连接,但它们在性能和用法上有一些关键区 ...

  4. 开源IM项目OpenIM单聊及万人群压测报告

    单聊压测结论: 华为云主机s3一台:8核16G内存,网络带宽10Mb,普通磁盘(非SSD) 同时在线及压测客户端数量:1万 每秒钟发送消息量:2300条: 从发送到对方接收平均消息延时:5秒 群聊压测 ...

  5. # 继续前行github star突破8k即时通讯IM开源项目OpenIM版本发布计划

    项目简介 OpenIM继续领跑开源IM领域,在广大开发者的支持下,目前github star突破8k.在数据泄露.信息外泄.隐私滥用的时代,IM私有化部署需求旺盛.其中,政企协同办公对IM需求猛增,随 ...

  6. rider代码折叠

    可折叠元素块 rider那些元素块是可折叠?参考官方文档:Fold Code Elements Code folding works for the keywords if/ while/ else/ ...

  7. 【4】python读写文件操作---详细讲解!

    相关文章: 全网最详细超长python学习笔记.14章节知识点很全面十分详细,快速入门,只用看这一篇你就学会了! [1]windows系统如何安装后缀是whl的python库 [2]超级详细Pytho ...

  8. 4.1 C/C++ 使用结构与指针

    C/C++语言是一种通用的编程语言,具有高效.灵活和可移植等特点.C语言主要用于系统编程,如操作系统.编译器.数据库等:C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统.图形用户界面 ...

  9. 自定义RecyclerView下拉刷新上拉加载更多

    自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多

  10. NC25084 [USACO 2006 Nov S]Bad Hair Day

    题目 题目描述 Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is s ...