1. 程式人生 > >布隆過濾器(海量資料找重複)

布隆過濾器(海量資料找重複)

1. 布隆過濾器

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

布隆過濾器是與雜湊演算法是相關的,是工業實踐上常用的演算法,之前我們使用HashMap或者HashSet來查詢重複的話也是可以的,但是對於在資料量比較大的情況下去查詢那麼速度就比較慢了,這個時候對於大的資料量來進行檢索使用布隆過濾查詢速度就比較快

 

2. 布隆過濾的思路:

先把已知的字串先儲存到點陣圖中,比如點陣圖的長度是100,那麼對應著0~99個位置,這些數字對應的位置可以使用二進位制的0或者1來表示

然後對於已知的字串我們都通過若干個的雜湊函式計算出雜湊值,然後再把雜湊值取點陣圖的長度使雜湊值落在點陣圖的範圍之中,這裡,把雜湊值對應位上的二進位制數字置為1

存進去之已知的字串之後那麼我們就可以得到已知字串的點陣圖了

查詢某個字串是否在這個集合之中那麼我們也是通過同樣的方法,通過同樣的若干個雜湊函式經過計算檢測對應的點陣圖上的二進位制數字是否為1,假如不是1那麼這個字串肯定不在這裡面,因為之前的字串對應的點陣圖上的二進位制位沒有一個與當前的字串進行匹配

假如檢測到當前字串的經過雜湊函式計算出來的雜湊值對應點陣圖上都為1,那麼這個時候可以大體上判斷當前的字串很有可能存在於這個集合之中,為什麼呢?因為之前的好幾個字串經過雜湊函式對映到點陣圖上的二進位制位為1那麼有可能是幾個字串公共對映得到的點陣圖上的二進位制位數為1的結果,所以不能夠與當前字串進行匹配,所以只能判斷當前字串很有可能存在而不是一定存在,存在一定的誤判率和誤識別率

下面使用md5演算法計算出雜湊值並在對應的點陣圖上置1,我們也可以使用其他的雜湊函式計算出雜湊值比如直接取餘法,乘法這些,使用任何的雜湊演算法計算出來的雜湊值對於檢索的結果是沒有影響的,只是檢索的效率會有所不同

並且這裡使用到了BigInteger類用來處理生成的比較大的數字

 

3. 具體的程式碼如下

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
    //點陣圖的長度
    public static final int NUM_SLOTS = 1024 * 1024 * 8;
    //雜湊函式的個數
    public static final int NUM_HASH  = 8;
    //初始化點陣圖
    private static BigInteger bits = new BigInteger("0"); 
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List<String> list = new ArrayList<String>();
        list.add("sddadsdaddsewwe");
        list.add("ddsdgsysahdsdys");
        list.add("eqtesdetwdghdsf");
        list.add("fhsjdfsdjfdhhgd");
        for(int i = 0; i < list.size(); i++){
            addElement(list.get(i));
        }
        System.out.println(check("ddadsdaddsewwe"));
        System.out.println(check("eqtesdetwdghdsf"));
        System.out.println(check("sfasga"));
        System.out.println(check("fhsjdfsdjfdhhgd"));
        sc.close();
    }

    private static void addElement(String string) {
        for(int i = 0; i < NUM_HASH; i++){
            int bit = hash(string, i);
            if(!bits.testBit(bit)){
                //左移將對應點陣圖上的位置為1
                bits = bits.or(new BigInteger("1").shiftLeft(bit));
            }
        }
    }

    private static int hash(String message, int index) {
        message += index;
        try {
            MessageDigest md5 = MessageDigest.getInstance("md5");
            byte bytes[] = message.getBytes();
            md5.update(bytes);
            byte bits[] = md5.digest();
            BigInteger bi = new BigInteger(bits);
            return Math.abs(bi.intValue()) % NUM_SLOTS;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return -1;
    }
    
    private static boolean check(String string) {
        //使用與填充點陣圖的方法一致檢查對應位上是否為一
        for(int i = 0; i < NUM_HASH; i++){
            int index = hash(string, i);
            if(!bits.testBit(index)){
                return false;
            }
        }
        return true;
    }
}