C++ 數據結構之布隆過濾器。本站提示廣大學習愛好者:(C++ 數據結構之布隆過濾器)文章只能為提供參考,不一定能成為您想要的結果。以下是C++ 數據結構之布隆過濾器正文
投稿:lqh
這篇文章主要介紹了C++ 數據結構之布隆過濾器的相關資料,需要的朋友可以參考下布隆過濾器
一、歷史背景知識
布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠超過一般的算法,缺點是有一定的誤識別率和刪除錯誤。而這個缺點是不可避免的。但是絕對不會出現識別錯誤的情況出現(即假反例False negatives,如果某個元素確實沒有在該集合中,那麼Bloom Filter 是不會報告該元素存在集合中的,所以不會漏報)
在 FBI,一個嫌疑人的名字是否已經在嫌疑名單上;在網絡爬蟲裡,一個網址是否被訪問過等等。最直接的方法就是將集合中全部的元素存在計算機中,遇到一個新 元素時,將它和集合中的元素直接比較即可。一般來講,計算機中的集合是用哈希表(hash table)來存儲的。它的好處是快速准確,缺點是費存儲空間。當集合比較小時,這個問題不顯著,但是當集合巨大時,哈希表存儲效率低的問題就顯現出來 了。
比如說,一個象 Yahoo,Hotmail 和 Gmai 那樣的公眾電子郵件(email)提供商,總是需要過濾來自發送垃圾郵件的人(spamer)的垃圾郵件。一個辦法就是記錄下那些發垃圾郵件的 email 地址。由於那些發送者不停地在注冊新的地址,全世界少說也有幾十億個發垃圾郵件的地址,將他們都存起來則需要大量的網絡服務器。如果用哈希表,每存儲一億 個 email 地址, 就需要 1.6GB 的內存(用哈希表實現的具體辦法是將每一個 email 地址對應成一個八字節的信息指紋,然後將這些信息指紋存入哈希表,由於哈希表的存儲效率一般只有 50%,因此一個 email 地址需要占用十六個字節。一億個地址大約要 1.6GB, 即十六億字節的內存)。因此存貯幾十億個郵件地址可能需要上百 GB 的內存。除非是超級計算機,一般服務器是無法存儲的[2]。
二、布隆過濾器原理以及優缺點
如果想判斷一個元素是不是在一個集合裡,一般想到的是將集合中所有元素保存起來,然後通過比較確定。鏈表、樹、散列表(哈希表,Hash table)等數據結構都是這種思想。但是隨著集合中元素的增加,我們需要的存儲空間越來越大。同時檢索速度也會越來越慢。
Bloom Filter 是一種空間效率很高的隨機數據結構,Bloom Filter 可以看做是對bit-map的擴展,它的原理是:
當一個元素被加入集合中時,通過K個hash函數將這個元素映射成一個位陣列(Bit array)中的K個點,將它們置成1. 檢索時,我們只需要看這些點是不是都是1就能(大約)知道集合中有沒有它:
如果這些點中有任何一個0,則被檢索元素一定不在;
如果都是1,則被檢索元素很可能在。
優點:
它的優點是空間效率和查詢時間都遠遠超過一般的算法,布隆過濾器存儲空間和插入\查詢時間都是O(K),另外,散列函數相互之間沒有關系,方便硬件並行實現,布隆過濾器不需要存儲元素本身,在某些對保密要求非常嚴格的場合有優勢。
缺點:
1、布隆過濾器的缺點和優點同樣明顯。誤算率是其中之一。隨著存入元素的增加,誤算率隨之增加。但是元素數量太少,則使用散列就可以了。
2、一般情況下不能從布隆過濾器中刪除元素,我們很容易想到把位數組變成整數數組,每插入一個元素相應的計數器加1,這樣刪除元素時將計數器減掉就可以了。然而要保證安全地刪除元素並非這麼簡單。首先我們必須保證刪除的元素的確存在布隆過濾器裡面,另外計數器回繞也會造成問題。
三、example
Google Chrome浏覽器使用Bloom filter識別惡意鏈接(能用較小的存儲空間表示較大的數據集合,簡單想就是把 每一個URL都可以映射成bit)的多,並且誤判率在萬分之一以下。
C++實現
bit_set.h
#pragma once #include<iostream> using namespace std; #include<vector> class Bitset { public: Bitset(size_t value) { _a.resize((value >> 5) + 1, 0); } bool set(size_t num) { size_t index = num>>5; size_t pos = num % 32; if (_a[index] & (1 << (31 - pos))) { return false; } else { _a[index] |= (1 << (31 - pos)); _size++; return true; } } bool Reset(size_t num) { size_t index = num >> 5; size_t pos = num % 32; if (Text(num)) { _a[index] &= ~(1 << (31 - pos)); _size--; return true; } else { return false; } } bool Text(size_t num) { size_t index = num >> 5; size_t pos = num % 32; return _a[index] & (1 << (31-pos)); } private: vector<int> _a; size_t _size; };
Hash.h
#pragma once template<class T> //各類哈希字符串轉換函數 size_t BKDRHash(const char *str) { register size_t hash = 0; while (size_t ch = (size_t)*str++) { hash = hash * 131 + ch; } return hash; } template<class T> size_t SDBMHash(const char *str) { register size_t hash = 0; while (size_t ch = (size_t)*str++) { hash = 65599 * hash + ch; } return hash; } template<class T> size_t RSHash(const char * str) { size_t hash = 0; size_t magic = 63689; while (size_t ch = (size_t)*str++) { hash = hash * magic + ch; magic *= 378551; } return hash; } template<class T> size_t APHash(const char *str) { register size_t hash = 0; size_t ch; for (long i = 0; ch = (size_t)*str++; i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ ch ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ ch ^ (hash >> 5))); } } return hash; } template<class T> size_t JSHash(const char* str) { if (!*str) { return 0; } size_t hash = 1315423911; while (size_t ch = (size_t)*str++) { hash ^= ((hash << 5) + ch + (hash >> 2)); } return hash; }
Bloom_Filter.h
#pragma once #include"bite_set.h" #include"Hash.h" #include<string> template<class T> struct __HashFunk1 { size_t operator()(const T& key) { return BKDRHash<T>(key.c_str()); } }; template<class T> struct __HashFunk2 { size_t operator()(const T& key) { return SDBMHash<T>(key.c_str()); } }; template<class T> struct __HashFunk3 { size_t operator()(const T& key) { return RSHash<T>(key.c_str()); } }; template<class T> struct __HashFunk4 { size_t operator()(const T& key) { return APHash<T>(key.c_str()); } }; template<class T> struct __HashFunk5 { size_t operator()(const T& key) { return JSHash<T>(key.c_str()); } }; template<class K = string, class HashFunk1 = __HashFunk1<K>, class HashFunk2 = __HashFunk2<K>, class HashFunk3 = __HashFunk3<K>, class HashFunk4 = __HashFunk4<K>, class HashFunk5 = __HashFunk5<K>> class BoolFilter { public: BoolFilter(size_t n) :_a(n * 10) , _range(n * 10) {} void set(const K& key) { _a.set(HashFunk1()(key) % _range); _a.set(HashFunk2()(key) % _range); _a.set(HashFunk3()(key) % _range); _a.set(HashFunk4()(key) % _range); _a.set(HashFunk5()(key) % _range); } bool Text(const K& key) { if (!_a.Text(HashFunk1()(key)% _range)) return false; if (!_a.Text(HashFunk2()(key) % _range)) return false; if (!_a.Text(HashFunk3()(key) % _range)) return false; if (!_a.Text(HashFunk4()(key) % _range)) return false; if (!_a.Text(HashFunk5()(key) % _range)) return false; return true; } private: Bitset _a; size_t _range; };