標籤:演算法
百度面試題:給定a、b兩個檔案,各存放50億個url,每個url各佔64位元組,記憶體限制是4G,讓你找出a、b檔案共同的url?
Bloom Filter是由Bloom在1970年提出的一種多雜湊函數映射的快速尋找演算法。通常應用在一些需要快速判斷某個元素是否屬於集合,但是並不嚴格要求100%正確的場合。
一. 執行個體
為了說明Bloom Filter存在的重要意義,舉一個執行個體:
(執行個體一),假設要你寫一個網路蜘蛛(web crawler)。由於網路間的連結錯綜複雜,蜘蛛在網路間爬行很可能會形成“環”。為了避免形成“環”,就需要知道蜘蛛已經訪問過那些URL。給一個URL,怎樣知道蜘蛛是否已經訪問過呢?稍微想想,
(執行個體二)給定a、b兩個檔案,各存放50億個url,每個url各佔64位元組,記憶體限制是4G,讓你找出a、b檔案共同的url?
就會有如下幾種方案:
1. 將訪問過的URL儲存到資料庫。
2. 用HashSet將訪問過的URL儲存起來。那隻需接近O(1)的代價就可以查到一個URL是否被訪問過了。
3. URL經過MD5或SHA-1等單向雜湊後再儲存到HashSet或資料庫。
4. Bit-Map方法。建立一個BitSet,將每個URL經過一個雜湊函數映射到某一位。
方法1~3都是將訪問過的URL完整儲存,方法4則只標記URL的一個映射位。
以上方法在資料量較小的情況下都能完美解決問題,但是當資料量變得非常龐大時問題就來了。
方法1的缺點:資料量變得非常龐大後關係型資料庫查詢的效率會變得很低。而且每來一個URL就啟動一次資料庫查詢是不是太小題大做了?
方法2的缺點:太消耗記憶體。隨著URL的增多,佔用的記憶體會越來越多。就算只有1億個URL,每個URL只算50個字元,就需要5GB記憶體。
方法3:由於字串經過MD5處理後的資訊摘要長度只有128Bit,SHA-1處理後也只有160Bit,因此方法3比方法2節省了好幾倍的記憶體。
方法4消耗記憶體是相對較少的,但缺點是單一雜湊函數發生衝突的機率太高。還記得資料結構課上學過的Hash表衝突的各種解決方案嗎?若要降低衝突發生的機率到1%,就要將BitSet的長度設定為URL個數的100倍。
實質上上面的演算法都忽略了一個重要的隱含條件:允許小機率的出錯,不一定要100%準確!也就是說少量url實際上沒有沒網路蜘蛛訪問,而將它們錯判為已訪問的代價是很小的——大不了少抓幾個網頁唄。
例如有 一組字元 arr:”哈哈“,”呵呵“........
字串:“哈哈”
雜湊演算法1處理後:8
雜湊演算法2處理後:1
雜湊演算法1處理後:3
插入BitArray後
再處理字串:“呵呵”
雜湊演算法1處理後:2
雜湊演算法2處理後:1
雜湊演算法1處理後:9
繼續插入BitArray後,如果繼續遊字串,繼續以這種方式插入
判斷”在這些字串是否包含”嘻嘻“
雜湊演算法1處理後:0
雜湊演算法2處理後:1
雜湊演算法1處理後:7
只要判斷 下標分別為 0,1,7位置的值是否都為1,如 因為位置0跟位置7的值不為1
所以”嘻嘻“不包含在arr中,反之如果都為1怎包含
java代碼實現如下
import java.util.ArrayList;import java.util.BitSet;import java.util.List;/** * BloomFilter演算法 * * @author JYC506 * */public class BloomFilter { /*雜湊函數*/private List<IHashFunction> hashFuctionList; /*構造方法*/public BloomFilter() {this.hashFuctionList = new ArrayList<IHashFunction>();} /*添加雜湊函數類*/public void addHashFunction(IHashFunction hashFunction) {this.hashFuctionList.add(hashFunction);} /*刪除hash函數*/public void removeHashFunction(IHashFunction hashFunction) {this.hashFuctionList.remove(hashFunction);} /*判斷是否被包含*/public boolean contain(BitSet bitSet, String str) {for (IHashFunction hash : hashFuctionList) {int hashCode = hash.toHashCode(str);if(hashCode<0){hashCode=-hashCode;}if (bitSet.get(hashCode) == false) {return false;}}return true;} /*添加到bitSet*/public void toBitSet(BitSet bitSet, String str) {for (IHashFunction hash : hashFuctionList) {int hashCode = hash.toHashCode(str);if(hashCode<0){hashCode=-hashCode;}bitSet.set(hashCode, true);}} public static void main(String[] args) {BloomFilter bloomFilter=new BloomFilter();/*添加3個雜湊函數*/bloomFilter.addHashFunction(new JavaHash());bloomFilter.addHashFunction(new RSHash());bloomFilter.addHashFunction(new SDBMHash());/*長度為2的24次方*/BitSet bitSet=new BitSet(1<<25);/*判斷test1很test2重複的字串*/String[] test1=new String[]{"哈哈","我","大家","逗比","有錢人性","小米","Iphone","helloWorld"};for (String str1 : test1) {bloomFilter.toBitSet(bitSet, str1);}String[] test2=new String[]{"哈哈","我的","大家","逗比","有錢的人性","小米","Iphone6s","helloWorld"};for (String str2 : test2) {if(bloomFilter.contain(bitSet, str2)){System.out.println("'"+str2+"'是重複的");}}System.out.println(new RSHash().toHashCode("哈哈"));System.out.println(new RSHash().toHashCode("aa"));}}/*雜湊函數介面*/interface IHashFunction {int toHashCode(String str);}class JavaHash implements IHashFunction {@Overridepublic int toHashCode(String str) {return str.hashCode();}}class RSHash implements IHashFunction {@Overridepublic int toHashCode(String str) {int b = 378551;int a = 63689;int hash = 0;for (int i = 0; i < str.length(); i++) {hash = hash * a + str.charAt(i);a = a * b;}return hash;}}class SDBMHash implements IHashFunction {@Overridepublic int toHashCode(String str) {int hash = 0;for (int i = 0; i < str.length(); i++)hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;return hash;}}
大資料處理演算法二:Bloom Filter演算法