1. 程式人生 > >資料結構--雜湊擴充套件 ( 布隆過濾器 )

資料結構--雜湊擴充套件 ( 布隆過濾器 )

布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進位制向量和一系列隨機對映函式。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。

基本概念 : 如果想要判斷一個元素是不是在一個集合裡,一般想到的是將所有元素儲存起來,然後通過比較確定。連結串列,樹等等資料結構都是這種思路. 但是隨著集合中元素的增加,我們需要的儲存空間越來越大,檢索速度也越來越慢(O(n),O(logn))。不過世界上還有一種叫作散列表(又叫雜湊表,Hashtable)的資料結構。它可以通過一個Hash函式

將一個元素對映成一個位陣列(Bitarray)中的一個點。這樣一來,我們只要看看這個點是不是1就可以知道集合中有沒有它了。這就是布隆過濾器的基本思想。

Hash面臨的問題就是衝突。假設Hash函式是良好的,如果我們的位陣列長度為m個點,那麼如果我們想將衝突率降低到例如 1%, 這個散列表就只能容納m / 100個元素。顯然這就不叫空間效率了(Space-efficient)了。解決方法也簡單,就是使用多個Hash,如果它們有一個說元素不在集合中,那肯定就不在。如果它們都說在,雖然也有一定可能性它們在說謊,不過直覺上判斷這種事情的概率是比較低的。

布隆過濾器的優點 :

  • 相比於其它的資料結構,布隆過濾器在空間和時間方面都有巨大的優勢。布隆過濾器儲存空間和插入/查詢時間都是
    常數
    。另外, 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;
}

除錯結果如下 : 在原來字串的基礎上做一些微小的變化來測試

若有出錯或不懂的地方,​ 歡迎留言, 共同進步 !