資料結構--雜湊擴充套件 ( 布隆過濾器 )
阿新 • • 發佈:2018-12-09
布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進位制向量和一系列隨機對映函式。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。
基本概念 : 如果想要判斷一個元素是不是在一個集合裡,一般想到的是將所有元素儲存起來,然後通過比較確定。連結串列,樹等等資料結構都是這種思路. 但是隨著集合中元素的增加,我們需要的儲存空間越來越大,檢索速度也越來越慢(O(n),O(logn))。不過世界上還有一種叫作散列表(又叫雜湊表,Hashtable)的資料結構。它可以通過一個Hash函式
Hash面臨的問題就是衝突。假設Hash函式是良好的,如果我們的位陣列長度為m個點,那麼如果我們想將衝突率降低到例如 1%, 這個散列表就只能容納m / 100個元素。顯然這就不叫空間效率了(Space-efficient)了。解決方法也簡單,就是使用多個Hash,如果它們有一個說元素不在集合中,那肯定就不在。如果它們都說在,雖然也有一定可能性它們在說謊,不過直覺上判斷這種事情的概率是比較低的。
布隆過濾器的優點 :
- 相比於其它的資料結構,布隆過濾器在空間和時間方面都有巨大的優勢。布隆過濾器儲存空間和插入/查詢時間都是
- 布隆過濾器可以表示全集,其它任何資料結構都不能
布隆過濾器的缺點 :
- 但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。但是如果元素數量太少,則使用散列表足矣。
- 另外,一般情況下不能從布隆過濾器中刪除元素。我們很容易想到把位列陣變成整數陣列,每插入一個元素相應的計數器加1, 這樣刪除元素時將計數器減掉就可以了。然而要保證安全的刪除元素並非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器裡面. 這一點單憑這個過濾器是無法保證的。另外計數器迴繞也會造成問題。
如下圖所示 : 布隆過濾器是一個元素對映多個位置 ;
布隆過濾器的實現也是需要依靠點陣圖為底層來實現
BloomFilter . c
#include "BitMap.h"
//元素第一個對映的位置
size_t BLFHashFun1(BitMap * blf, char* x)
{
assert(blf);
size_t ret = 0;
while (*x){
//31作為乘子
ret += ret * 31 + *x;
x++;
}
//取模,計算位置
return ret%blf->capacity;
}
//元素第二個對映的位置
size_t BLFHashFun2(BitMap * blf, char* x)
{
assert(blf);
size_t ret = 0;
while (*x){
//131作為乘子
ret += ret * 131 + *x;
x++;
}
//取模,計算位置
return ret%blf->capacity;
}
//元素第三個對映的位置
size_t BLFHashFun3(BitMap * blf, char* x)
{
assert(blf);
size_t ret = 0;
while (*x){
//51作為乘子
ret += ret * 51 + *x;
x++;
}
//取模,計算位置
return ret%blf->capacity;
}
//布隆過濾器初始化
void BLFInit(BitMap * blf, size_t capacity)
{
assert(blf);
//要對映三個位置,為了降低誤算率,容量開闢原來的三倍
BMPInit(blf, capacity * 3);
}
//布隆過濾器銷燬
void BLFDestory(BitMap * blf)
{
assert(blf);
BMPDestory(blf);
}
//布隆過濾器儲存
void BLFInsert(BitMap * blf, char* x)
{
//將要對映的三個位置找出然後分別儲存
assert(blf);
int index1 = BLFHashFun1(blf, x);
int index2 = BLFHashFun2(blf, x);
int index3 = BLFHashFun3(blf, x);
BMPInsert(blf, index1);
BMPInsert(blf, index2);
BMPInsert(blf, index3);
}
//由於布隆過濾器中元素的對映是互相影響的,所以不能直接刪除
//某一個元素的對映
//void BLFResert(BLF * blf, size_t x);
//檢查元素是否存在
//這裡要判斷一個元素是否存在,它對映的三個為必須均為1
//這要有一位不滿足條件,則元素不存在
//這裡如果判斷出一個元素存在,結果是不準確定的,而當一個
//元素被判斷不存在時,結果一定是準確的
int BLFCheck(BitMap * blf, char* x)
{
assert(blf);
//檢查第一個對映位
int index1 = BLFHashFun1(blf, x);
if (!BMPCheck(blf, index1))
return 0;
//檢查第二個對映位
int index2 = BLFHashFun2(blf, x);
if (!BMPCheck(blf, index2))
return 0;
//檢查第三個對映位
int index3 = BLFHashFun3(blf, x);
if (!BMPCheck(blf, index3))
return 0;
printf("字串對映的三個位置分別為 : %d %d %d \n", index1, index2, index3);
return 1;
}
//測試
//如果存在返回1,不存在返回0
void BLFTest()
{
BitMap blf;
BLFInit(&blf, 50);
BLFInsert(&blf, "abc");
BLFInsert(&blf, "sdghjddh k");
BLFInsert(&blf, "https://blog.csdn.net/ds19980 228");
printf("%d\n", BLFCheck(&blf, "abc"));
printf("%d\n", BLFCheck(&blf, "acb"));
printf("%d\n", BLFCheck(&blf, "sdghjddh k"));
printf("%d\n", BLFCheck(&blf, "sdghjddhk "));
printf("%d\n", BLFCheck(&blf, "https://blog.csdn.net/ds1998 0228"));
printf("%d\n", BLFCheck(&blf, "https://blog.csdn.net/ds199802 28"));
BLFDestory(&blf);
}
這裡的 BloomFilter . h 的標頭檔案就是 BitMap . h 的標頭檔案
#pragma once
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//資料結構
typedef struct BitMap
{
char * data;
//點陣圖最大能表示的位元位數
size_t capacity;
}BitMap;
//點陣圖的初始化
void BMPInit(BitMap * bmp, size_t capacity);
//點陣圖的銷燬
void BMPDestory(BitMap * bmp);
//點陣圖的儲存
void BMPInsert(BitMap * bmp, size_t x);
//點陣圖的刪除
void BMPResert(BitMap * bmp, size_t x);
//檢查元素是否存在
int BMPCheck(BitMap * bmp, size_t x);
//測試
void BMPTest();
//布隆過濾器初始化
void BLFInit(BitMap * bmp, size_t capacity);
//布隆過濾器銷燬
void BLFDestory(BitMap * bmp);
//布隆過濾器儲存
void BLFInsert(BitMap * bmp, char* x);
//void BLFResert(BitMap * bmp, char x);
//檢查元素是否存在
int BLFCheck(BitMap * bmp, char* x);
//測試
void BLFTest();
Test . c 檔案
#include "BitMap.h"
int main()
{
BLFTest();
system("pause");
return 0;
}
除錯結果如下 : 在原來字串的基礎上做一些微小的變化來測試
若有出錯或不懂的地方, 歡迎留言, 共同進步 !