布隆過濾器
布隆過濾器是可以用於判斷一個元素是不是在一個集合裏,並且相比於其它的數據結構,布隆過濾器在空間和時間方面都有巨大的優勢。
優點:佔用空間小,查詢快
缺點:有誤判,刪除困難
具體原理:鏈接
簡單易懂的描述:
布隆過濾器。其實現方法就是:利用內存中一個長度爲M的位數組B並初始化裏面的所有位都爲0,如下面的表格所示:
0 0 0 0 0 0 0 0 0 0
然後我們根據H個不同的散列函數,對傳進來的字符串進行散列,並且每次的散列結果都不能大於位數組的長度。布隆過濾器的誤判率取決於你使用多少個不同的散列函數。現在我們先假定有4個不同散列函數,傳入一個字符串並進行一次插入操作,這時會進行4次散列,假設到了4個不同的下標,這個時候我們就會去數組中,將這些下標的位置置爲1,數組變更爲:
0 1 0 1 1 0 0 0 0 1
如果接下來我們再傳入同一個字符串時,因爲4次的散列結果都是跟上一次一樣的,所以會得出跟上面一樣的結果,所有應該置1的位都已經置1了,這個時候我們就可以認爲這個字符串是已經存在的了。因此不難發現,這是會存在一定的誤判率的,具體由你採用的散列函數質量,以及散列函數的數量確定。
最最原始的Java實現:
只做過簡單測試(嗯。。。應該沒問題吧)
import java.util.BitSet;
/**
* 最最原始的布隆過濾器類
*/
public class SimpleBloomFilter
{
// 設置布隆過濾器的大小
private static final int DEFAULT_SIZE = 2 << 24;
// 產生隨機數的種子,可產生6個不同的隨機數產生器。。。而且最好取素數
private static final int[] seeds = new int[]{7, 11, 13, 31, 37, 61};
// Java中的按位存儲的思想,其算法的具體實現(布隆過濾器)
private BitSet bits = new BitSet(DEFAULT_SIZE);
// 根據隨機數的種子,創建6個哈希函數
private SimpleHash[] func = new SimpleHash[seeds.length];
// 設置布隆過濾器所對應k(6)個哈希函數
public SimpleBloomFilter()
{
for (int i = 0; i < seeds.length; i++)
{
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
}
public static void main(String[] args)
{
SimpleBloomFilter filter = new SimpleBloomFilter();
filter.add("123");
System.out.println(filter.contains("123"));
System.out.println(filter.contains("124"));
filter.add("124");
System.out.println(filter.contains("124"));
}
/**
* 添加
* @param str 值
*/
public void add(String str)
{
// 集齊6個hash值,準備(召喚神龍)添加
for (SimpleHash f:func)
{
bits.set(f.hash(str));
}
}
/**
* 判斷是否存在
* @param str
* @return
*/
public boolean contains(String str)
{
// 根據此URL得到在布隆過濾器中的對應位,並判斷其標誌位(6個不同的哈希函數產生6種不同的映射)
for (SimpleHash f : func)
{
//當存在六位不都爲0時,返回false
if (!bits.get(f.hash(str)))
{
return false;
}
}
return true;
}
/**
* 哈希類
*/
public static class SimpleHash
{
private int cap;
private int seed;
// 默認構造器,哈希表長默認爲DEFAULT_SIZE大小,此哈希函數的種子爲seed
public SimpleHash(int cap, int seed)
{
this.cap = cap;
this.seed = seed;
}
public int hash(String value)
{
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++)
{
// 散列函數...重點,將此URL(使用到了集合中的每一個元素)散列到一個值
result = seed * result + value.charAt(i);
}
// 產生單個信息指紋。。。不能直接返回result,可能會越界
return (cap - 1) & result;
}
}
}
思考:
1.雖然布隆過濾器的數學原理好像很複雜的樣子,但是真正實現起來並不是特別困難。。。。只是數學證明與描述。。。。。真的有點難
2.有所得,必有所失
就像基於比較的排序算法的時間複雜度下限是O(nLgn)一樣,滿足100%正確率,時間複雜度爲O(1)的去重算法的空間複雜度下限應該是很接近於簡單的hash算法了(沒找到論文,不負責任的猜測一下)
而想要進一步降低空間複雜度,可能就需要放寬去重條件的限制了,比如去掉時間複雜度爲O(1),那麼做到空間複雜度爲O(1)都是很簡單的事。
而布隆過濾器就相當於去掉了100%正確率的條件限制,換取了空間複雜度的進一步縮小。