【C++】位圖/布隆過濾器+海量數據處理

目錄

一、位圖

1.1 位圖的概念

1.2 位圖的實現

1.3 位圖的應用(面試題)

二、布隆過濾器

2.1 布隆過濾器的引入

2.2?布隆過濾器概念

2.3 布隆過濾器的插入和查找

2.4 布隆過濾器的實現

2.5 布隆過濾器的優點和缺陷

2.6 布隆過濾器的應用(面試題)


一、位圖

1.1 位圖的概念

在C++中,位圖(Bitmap)是一種數組,其中數組的每個元素可以表示多個布爾值。用于高效地存儲和查詢海量數據,其中元素的每個布爾值只占用一個位(bit)的空間。通常是用來判斷某個數據存不存在的。

例如在32位系統中,一個unsigned int類型的變量可以表示32個布爾值。通過位操作,我們可以檢查、設置或清除特定的位,從而實現對大量布爾值的快速訪問和修改。

面試題
給40億個不重復的無符號整數,沒排過序。給一個無符號整數,如何快速判斷一個數是否在
這40億個數中。【騰訊】

方法:

1. 遍歷,時間復雜度O(N)
2. 排序(O(NlogN)),利用二分查找: logN
3. 位圖解決

方法1和2中都需要將數據保存在數組中,40億個整數需要16G內存,內存開辟不了這么大的連續空間。
數據是否在給定的整形數據中,結果是在或者不在,剛好是兩種狀態,那么可以使用一
個二進制比特位來代表數據是否存在的信息,如果二進制比特位為1,代表存在,為0
代表不存在。
比如:

40億個不重復的無符號整數的范圍在0 ~ 2^32,有42億多種可能性。一個字符型8個比特位,每個比特位來存儲一個數字是否存在的狀態,需要(2^32) / 8 = 2^29字節,即0.5G的內存。

1.2 位圖的實現

C++標準庫中的位圖在線文檔說明? ? ?#include <bitset>

在C++中,位圖的實現通常使用unsigned int數組或者std::vector<unsigned int>。下面是一個簡單的位圖實現:

// N是需要多少比特位
template<size_t N>
class myBitSet
{
public:myBitSet(){_bs.resize((N >> 5) + 1, 0);//一個size_t 4個字節,32個比特位,右移五位相當于除以32,+1是防止有余數//注意:右移運算符>>優先級高于+,需要在右移時加上括號}void set(size_t x)//對第x個比特位置為1{size_t i = x / 32;//確定在哪一個size_tsize_t j = x % 32;//確定在哪一個比特位上_bs[i] |= (1 << j);}void reset(size_t x)//第x個比特位置清0{size_t i = x / 32;//確定在哪一個size_tsize_t j = x % 32;//確定在哪一個比特位上_bs[i] &= ~(1 << j);}bool test(size_t x)//返回第x位的狀態{size_t i = x / 32;//確定在哪一個size_tsize_t j = x % 32;//確定在哪一個比特位上return _bs[i] & (1 << j);}private:vector<size_t> _bs;
};

1.3 位圖的應用(面試題)

1. 快速查找某個數據是否在一個集合中
2. 排序 + 去重
3. 求兩個集合的交集、并集等
4. 操作系統中磁盤塊標記

1. 給定100億個整數,設計算法找到只出現一次的整數?

100一個整數,它的范圍也是只在0 ~ 2^32 之間,可以設計兩個位圖,每一次映射都調整位圖里面的數字。例如出現0次設為00,出現1次設為01,二次及以上都為10。

template<size_t N>
class twobitset
{
public:void set(size_t x){//00->01//01->10//10->不變if (_bs1.test(x) == false && _bs2.test(x) == false)//出現0次{_bs2.set(x);//新增設為01}else if (_bs1.test(x) == false && _bs2.test(x) == true)//出現1次{_bs1.set(x);_bs2.reset(x);//新增設為10}//出現2次及以上}void PrintOnce(){for (size_t i = 0; i < N; i++){if (_bs1.test(i) == false && _bs2.test(i) == true){cout << i << endl;}}cout << endl;}private:myBitSet<N> _bs1;myBitSet<N> _bs2;
};

2. 給兩個文件,分別有100億個整數,我們只有1G內存,如何找到兩個文件交集?

各自映射到一個位圖,若一個值在兩個位圖都存在,則是交集

3. 位圖應用變形:1個文件有100億個int,1G內存,設計算法找到出現次數不超過2次的所有整數。

方法同問題1,創建兩個位圖,出現0次00,出現1次01,出現2次10,出現3次及以上 11。

二、布隆過濾器

2.1 布隆過濾器的引入

之前的搜索方法中,傳統的數據結構和方法存在一定的局限性:

  • 暴力查找:數據量大了,效率就低。O(n)
  • 排序+二分查找:排序有代價、數組不方便增刪。雖然查找的時間復雜度為 O(log n),但排序的時間復雜度為 O(n log n)
  • 搜索樹 ->AVL樹+紅黑樹:隨著數據量的增加,樹的高度也會增加,導致性能下降。
  • 哈希:當發生哈希碰撞時,性能會下降。此外,哈希表的空間消耗相對較高。
  • 以上數據結構,空間消耗很高。如何應對數量很大的數據?
  • 位圖及變形:位圖只能處理整數,對于其他類型的數據則不適用。

如果要應對其他類型數據的問題,如何快速查找?

  • 用哈希表存儲用戶記錄,缺點:浪費空間
  • 用位圖存儲用戶記錄,缺點:位圖一般只能處理整形,如果內容編號是字符串,就無法處理了。
  • 將哈希與位圖結合,即布隆過濾器。

2.2?布隆過濾器概念

布隆過濾器(Bloom Filter)是一種由布隆在1970年提出的概率型數據結構,它通過使用多個哈希函數將元素映射到一個 bit 數組中來實現高效的數據查詢和插入。布隆過濾器的核心優勢在于它的空間效率:它使用相對較少的內存來表示一個可能非常大的集合。可以用來告訴你 “某樣東西 一定不存在 或者 可能存在”。

布隆過濾器的特點包括:

  • 高效插入和查詢:插入和查詢操作都非常快速,因為它們只涉及到哈希計算和位操作。
  • 概率性:布隆過濾器可能會返回誤報(即一個元素被錯誤地判斷為存在于集合中),但不會返回漏報(即如果一個元素被判斷為不存在,那么它一定不存在)。
  • 固定大小:布隆過濾器的位數組大小在創建時確定,不會隨著元素的添加而改變。
  • 不可刪除:一旦元素被插入布隆過濾器,就無法刪除,因為刪除可能會導致其他元素的位被錯誤地設置為0。(傳統布隆過濾器不可刪除)

舉個例子:原本在位圖或哈希表/桶中,可能出現多個關鍵字映射到同一個位置,現在使用多個哈希函數,讓原本映射到一個位置的結果映射到多個位置。檢查關鍵字在不在,要把關鍵字映射的所有位置都檢查一遍,只有所有位置的狀態都為1,這個關鍵字才 “可能存在”,如果映射的位置有一個檢測到了0,那么這個關鍵字肯定不存在。

詳解布隆過濾器的原理,使用場景和注意事項

上面鏈接的講解非常精彩,有興趣的話可以看一下。

2.3 布隆過濾器的插入和查找

布隆過濾器是一個 bit 向量或者說 bit 數組

如果我們要映射一個值到布隆過濾器中,我們需要使用多個不同的哈希函數生成多個哈希值,并對每個生成的哈希值指向的 bit 位置為?1,例如針對值 “baidu” 和三個不同的哈希函數分別生成了哈希值 1、4、7,則上圖轉變為:

在再存一個值 “tencent”,如果哈希函數返回 3、4、8 的話,圖繼續變為:

值得注意的是,4 這個 bit 位由于兩個值的哈希函數都返回了這個 bit 位,因此它被覆蓋了。現在我們如果想查詢 “dianping” 這個值是否存在,哈希函數返回了 1、5、8三個值,結果我們發現 5 這個 bit 位上的值為 0,說明沒有任何一個值映射到這個 bit 位上,因此我們可以很確定地說 “dianping” 這個值不存在。而當我們需要查詢 “baidu” 這個值是否存在的話,那么哈希函數必然會返回 1、4、7,然后我們檢查發現這三個 bit 位上的值均為 1,那么我們可以說 “baidu” 存在了么?答案是不可以,只能是 “baidu” 這個值可能存在。

這是為什么呢?答案跟簡單,因為隨著增加的值越來越多,被置為 1 的 bit 位也會越來越多,這樣某個值 “taobao” 即使沒有被存儲過,但是萬一哈希函數返回的三個 bit 位都被其他值置位了 1 ,那么程序還是會判斷 “taobao” 這個值存在。

2.4 布隆過濾器的實現

各種字符串Hash函數

選取3種平均分較高的哈希算法:BKDRHash,APHash,DJBHash。

#include "myBitSet.h"struct BKDRHash
{size_t operator()(const string& key){// BKDRsize_t hash = 0;for (auto e : key){hash *= 31;hash += e;}return hash;}
};struct APHash
{size_t operator()(const string& key){size_t hash = 0;for (size_t i = 0; i < key.size(); i++){char ch = key[i];if ((i & 1) == 0){hash ^= ((hash << 7) ^ ch ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};struct DJBHash
{size_t operator()(const string& key){size_t hash = 5381;for (auto ch : key){hash += (hash << 5) + ch;}return hash;}
};template <size_t N, class K = string, class HashFunc1 = BKDRHash, class HashFunc2 = APHash, class HashFunc3 = DJBHash>
class myBloomFilter
{
public:void set(const K& key){size_t hash1 = HashFunc1()(key) % N;size_t hash2 = HashFunc2()(key) % N;size_t hash3 = HashFunc3()(key) % N;_bs.set(hash1);_bs.set(hash2);_bs.set(hash3);}// 一般不支持刪除,刪除一個值可能會影響其他值// 非要支持刪除,也是可以的,用多個位標記一個值,存引用計數// 但是這樣話,空間消耗的就變大了void reset(const K& key);bool test(const K& key){//判斷不存在是準確的size_t hash1 = HashFunc1()(key) % N;if (_bs.test(hash1) == false)return false;size_t hash2 = HashFunc2()(key) % N;if (_bs.test(hash2) == false)return false;size_t hash3 = HashFunc3()(key) % N;if (_bs.test(hash3) == false)return false;return true;//有可能誤判}private:myBitSet<N> _bs;
};

2.5 布隆過濾器的優點和缺陷

布隆過濾器優點
1. 增加和查詢元素的時間復雜度為:O(K), (K為哈希函數的個數,一般比較小),與數據量大小無關
2. 哈希函數相互之間沒有關系,方便硬件并行運算
3. 布隆過濾器不需要存儲元素本身,在某些對保密要求比較嚴格的場合有很大優勢
4. 在能夠承受一定的誤判時,布隆過濾器比其他數據結構有這很大的空間優勢
5. 數據量很大時,布隆過濾器可以表示全集,其他數據結構不能
6. 使用同一組散列函數的布隆過濾器可以進行交、并、差運算

布隆過濾器缺陷
1. 有誤判率,即存在假陽性(False Position),即不能準確判斷元素是否在集合中(補救方法:再建立一個白名單,存儲可能會誤判的數據)
2. 不能獲取元素本身
3. 一般情況下不能從布隆過濾器中刪除元素
4. 如果采用計數方式刪除,可能會存在計數回繞問題

2.6 布隆過濾器的應用(面試題)

布隆過濾器使用場景:可以接受誤判的場景

1. 給兩個文件,分別有100億個query,我們只有1G內存,如何找到兩個文件交集?分別給出精確算法和近似算法。

query 是字符串,假設平均1個query是50byte,1G 約等于10億byte,100億個query就是5000億byte ,約等于 500G,內存根本存不下。

500G的文件存放不下,可以將它們拆分成多個小文件,例如500個小文件a0~a499,每個文件大小就是1G。讀取第一個文件的每個query,使用hash函數將query映射到500個文件中(計算 i = Hash(query) % 500),i 是幾,query就進入第幾個小文件。

兩個文件中相同的query分別進入了編號相同的小文件 ai 和 bi。

兩個大文件對應的編號相同的小文件ai 和 bi求交集,可以分別插入到一個setA和一個setB中,快速找到交集。

但是這樣也可能有其他問題,例如某個文件可能太大,原因:

  1. 小文件中大多數都是某一個query,重復過多。
  2. 小文件有很多不同的query。

如果是情況1,文件很大有很多重復,后面重復播入都失敗,可以插入到set,set可以去重。如果是情況2,不斷插入set以后,內存不足,會拋異常,需要換一個哈希函數進行二次切分,再找交集。

近似算法(布隆過濾器)
1. 創建布隆過濾器:由于1G內存大約可以表示10億字節,即約80億bit,我們可以創建一個布隆過濾器,使用足夠多的哈希函數(例如4到6個),以保持較低的誤報率。

2. 插入第一個文件的query:遍歷第一個文件中的每個query,對每個query應用哈希函數,并將對應位設置為1。

3. 查詢第二個文件的query:遍歷第二個文件中的每個query,同樣應用哈希函數。如果布隆過濾器中的所有對應位都是1,則認為該query可能出現在第一個文件中。

4. 輸出可能交集:將所有可能出現在交集中的query輸出到新文件中。

5. 誤報處理:由于布隆過濾器的性質,輸出文件中可能包含一些誤報,即實際上并不在第一個文件中的query。可以通過調整布隆過濾器的參數來減少誤報率。

精確算法(哈希分割)
1文件分割:對兩個文件的query進行哈希處理,然后使用除留余數法(%5000)將query分布到5000個不同的子文件中。每個子文件的大小大約為100M,這樣每個文件的總大小就是5000 * 100M = 500G,符合原始數據的大小。

2.讀取和比較:對于第二個文件中的每個query,同樣進行哈希處理,然后將query放入對應的子文件中。接著,對于每個子文件,將其內容讀取到內存中的unordered_map中,然后查找第一個文件中的對應子文件,看是否有相同的query。
3.輸出交集:如果在一個子文件中找到了匹配的query,那么這個query就屬于兩個文件的交集。將這些query輸出到新文件中。
4.內存使用:由于每個子文件是獨立處理的,所以每次只需要將一個子文件的內容讀取到內存中,這樣內存的使用就被限制在了100M左右,符合1G內存的限制。
精確算法的關鍵在于將大文件分割成多個小文件,然后逐個處理,這樣可以避免一次性將所有數據加載到內存中。這種方法雖然準確,但是處理時間可能會比近似算法長,因為需要多次讀寫磁盤。

2. 給一個超過100G大小的log file, log中存著IP地址, 設計算法找到出現次數最多的IP地址?

與上一題同理,i= Hash(ip) % 100,這個ip就進入Ai小文件。相同的ip一定進入了同一個小文件,我們對小文件用map統計出ip次數就是準確的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13747.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13747.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13747.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Servlet 的 API

HttpServlet init&#xff1a;當 tomcat 收到了 /hello 這樣的路徑是請求后就會調用 HelloServlet&#xff0c;于是就需要對 HelloServlet 進行實例化&#xff08;只實例一次&#xff0c;后續再有請求也不實例了&#xff09;。 destory&#xff1a;如果是通過 smart tomcat 的停…

實驗六 Java流式編程與網絡程序設計 頭歌

實驗六 Java流式編程與網絡程序設計 頭歌 制作不易&#xff01;點個關注&#xff01;給大家帶來更多價值&#xff01; 第1關 字節輸入/輸出流實現數據的保存和讀取 package step1;import java.io.*; import java.util.*;public class SortArray {public static void main(St…

洗地機品牌哪個牌子好?實力派洗地機品牌TOP10榜單

洗地機依靠其洗、拖、吸、烘為一體的功能&#xff0c;能高效的完成地面清潔的工作&#xff0c;深受大家的喜愛。但是洗地機的型號越來越多&#xff0c;功能也越來越多&#xff0c;對于不想花大價錢&#xff0c;又想要高性價比的精致人群來說實在不友好&#xff0c;所以筆者今天…

C++ 中重寫重載和隱藏的區別

重寫&#xff08;override&#xff09;、重載&#xff08;overload&#xff09;和隱藏&#xff08;overwrite&#xff09;在C中是3個完全不同的概念。我們這里對其進行詳細的說明 1、重寫&#xff08;override&#xff09;是指派生類覆蓋了基類的虛函數&#xff0c;這里的覆蓋必…

如何寫好科研論文(討論)

討論1. 如何去選取第一批要閱讀的論文&#xff1f; 當我選擇第一批要閱讀的論文時&#xff0c;我會遵循以下幾個步驟&#xff0c;以確保所選的論文對我的研究最有幫助&#xff1a; 研究問題的相關性&#xff1a; 明確我的研究問題或主題&#xff1a;首先&#xff0c;我會確保自…

實例展示vue單元測試及難題解惑

通過生動詳實的例子帶你排遍vue單元測試過程中的所有疑惑與難題。 技術棧&#xff1a;jest、vue-test-utils。 共四個部分&#xff1a;運行時、Mock、Stub、Configuring和CLI。 運行時 在跑測試用例時&#xff0c;大家的第一個絆腳石肯定是各種undifned報錯。 解決這些報錯…

【HarmonyOS4學習筆記】《HarmonyOS4+NEXT星河版入門到企業級實戰教程》課程學習筆記(九)

課程地址&#xff1a; 黑馬程序員HarmonyOS4NEXT星河版入門到企業級實戰教程&#xff0c;一套精通鴻蒙應用開發 &#xff08;本篇筆記對應課程第 16 節&#xff09; P16《15.ArkUI-狀態管理-任務統計案例》 1、實現任務進度卡片 怎么讓進度條和進度展示文本堆疊展示&#xff1…

./scripts/Makefile.clean 文件分析

文章目錄 目標 $(subdir-ymn)目標__clean $(clean-dirs): ??? make -f ./scripts/Makefile.clean obj$(patsubst _clean_%,%,$) $(clean-dirs)$(patsubst _clean_%,%,$)_clean_api _clean_cmd _clean_common _clean_disk _clean_drivers _clean_drivers/ddr/altera _clean_d…

react中的useEffect()的使用

useEffect()是react中的hook函數&#xff0c;作用是用于創建由渲染本身引起的操作&#xff0c;而不是事件的觸發&#xff0c;比如Ajax請求&#xff0c;DOM的更改 首先useEffect()是個函數&#xff0c;接受兩個參數&#xff0c;第一個參數是一個方法&#xff0c;第二個參數是數…

數據結構--樹與二叉樹--編程求以孩子兄弟表示法存儲的森林的葉結點個數

數據結構–樹與二叉樹–編程求以孩子兄弟表示法存儲的森林的葉結點個數 題目 編程求以孩子兄弟表示法存儲的森林的葉結點個數 ps&#xff1a;題目來源2025王道數據結構 思路 樹上的操作大多數是通過遞歸進行的 我們可以從根節點開始遞歸 如果結點 N 沒有孩子指針&#xff…

【Entity Framework】如何理解EF中的級聯刪除

【Entity Framework】如何理解EF中的級聯刪除 文章目錄 【Entity Framework】如何理解EF中的級聯刪除一、概述二、發生級聯行為時2.1/刪除主體/父實體2.2/斷開關系 三、發生級聯行為的位置3.1/級聯刪除被跟蹤實體3.2/數據庫中的級聯刪除 四、級聯NULL 一、概述 Entity Framewo…

vue3 路由跳轉 攜帶參數

實現功能&#xff1a;頁面A 跳轉到 頁面B&#xff0c;攜帶參數 路由router.ts import { createRouter, createWebHistory } from "vue-router";const routes: RouteRecordRaw[] [{path: "/demo/a",name: "aa",component: () > import(&quo…

x264 碼率控制原理:x264_ratecontrol_start 函數

x264_ratecontrol_start 函數 函數原理 函數功能:編碼一幀之前,為當前幀選擇一個量化 QP,屬于幀級別碼率控制;這對于控制視頻質量和文件大小至關重要。通過調整QP,編碼器可以在保持視頻質量的同時,盡可能減小輸出文件的大小。函數參數:x264_t *h: 編碼器上下文結構體指…

十七、個人信息出境標準合同的具體內容有哪些?

根據《標準合同辦法》第六條&#xff0c;標準合同應當嚴格按照網信辦制定版本訂立&#xff0c;個人信息處理者可以與境外接收方約定其他條款&#xff0c;但不得與標準合同相沖突。 根據《標準合同辦法》附件&#xff0c;目前版本的標準合同內容主要包括&#xff1a; 1. 個人信…

Flutter 中的 TextButton 小部件:全面指南

Flutter 中的 TextButton 小部件&#xff1a;全面指南 在Flutter的世界里&#xff0c;TextButton是一個基礎的小部件&#xff0c;用于創建只包含文本的按鈕。它通常用于對話框、表單以及需要強調主要操作的界面。本文將為您提供一個全面的指南&#xff0c;幫助您了解如何使用T…

地信遙感測繪電子書

《地理信息系統概論》&#xff0c;黃杏元&#xff0c;馬勁松編著&#xff0c;第三版&#xff0c;高等教育出版社&#xff0c;2008年 《地理信息系統》&#xff08;第二版&#xff09;湯國安&#xff0c;趙牡丹&#xff0c;楊昕等編&#xff0c;高等教育出版社&#xff0c;2010…

【stm32/CubeMX、HAL庫】嵌入式實驗五:定時器(2)|PWM輸出

參考&#xff1a; 【【正點原子】手把手教你學STM32CubeIDE開發】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系統基礎與實踐》劉黎明等編著&#xff0c;第九章定時器&#xff0c…

8操作系統定義、分類及功能+設備管理+作業管理 軟設刷題 軟考+

操作系統定義、分類及功能設備管理作業管理 知識點1-55-1010-1515-2020-2525-3030-35 刷題操作系統定義、分類及功能1-55-1010-15作業管理1-5設備管理1-55-10 知識點 1-5 1 嵌入式操作系統的特點&#xff1a; 1.微型化&#xff0c;從性能和成本角度考慮&#xff0c;希望占用的…

145.棧和隊列:刪除字符串中的所有相鄰重復項(力扣)

題目描述 代碼解決 class Solution { public:string removeDuplicates(string s) {// 定義一個棧來存儲字符stack<char> st;// 遍歷字符串中的每一個字符for(int i 0; i < s.size(); i){// 如果棧為空或棧頂字符與當前字符不相同&#xff0c;則將當前字符入棧if(st.e…

Jenkins的Pipeline流水線

目錄 前言 流水線概念 什么是流水線 Jenkins流水線 pipeline node stage step 創建一個簡單的流水線 創建Pipeline項目 選擇模板 測試 前言 提到 CI 工具&#xff0c;首先想到的就是“CI 界”的大佬——Jenkjns,雖然在云原生爆發的年代,蹦出來了很多云原生的 CI 工具…