布隆过滤器

Bloom Filter 布隆过滤器

1)

    最近在用python写一些爬虫程序,当需要通过html的url爬取所有层级链接的页面时.经常会遇到相同的url,这会导致很多页面重复爬取。因此需要设计算法,过滤掉已经下载过的url,大概的实现方法有:
  1. 将下载过的url保存到数据库,作为主键或类似的方法,保证其不重复
  2. 使用HashSet集合保证唯一性,可大大提高查询效率
  3. 对URL做摘要处理(例如MD5,SHA-1等算法),然后存储其映射
  4. 建立一个BitSet集合,将url通过hash函数映射到某一位
    方法1在大量数据的情况下,查询速度堪忧.方法2 方法3又会占用大量内存.相比之下方法4,通过hash函数,只存储一个位标记,查询速度和存储上的问题都得到了解决,唯一的问题就是当大数据量时,较大概率会出现映射的冲突,导致查询错误。

2)

    布隆过滤器,采用了方法4的思想,但是其采用了多个不同的hash函数来解决冲突问题。
    由此可见,布隆过滤器,就是由一个位数组,以及n个hash函数构成的。当位url经过hash函数运算,对应位的值为0时,说明该url未被使用,反之,则表示已经使用过。当然,由于一定概率的冲突,会导致未使用过的url也映射到1位。但是对于使用过的url,绝对不会被映射到0位。因此,布隆过滤器适用于有一定容错的查重场景。
    在爬取网页的情景中,其会导致少量的网页不会被爬取,但不会爬取已爬取过的网页。此缺点完全可以被接受。

3)

     其java实现:
import java.util.BitSet;

public class BloomFilter {
    /* BitSet初始分配2^24个bit */ 
    private static final int DEFAULT_SIZE =1<<25; 
    /* 不同哈希函数的种子,一般应取质数 */
    private static final int[] seeds =new int[] { 5, 7, 11, 13, 31, 37, 61 };
    private BitSet bits =new BitSet(DEFAULT_SIZE);
/* 哈希函数对象 */ 
    private SimpleHash[] func =new SimpleHash[seeds.length];

    public BloomFilter() {
        for (int i =0; i < seeds.length; i++){
            func[i] =new SimpleHash(DEFAULT_SIZE, seeds[i]);
        }
    }

    // 将字符串标记到bits中
    public void add(String value) {
        for (SimpleHash f : func) {
            bits.set(f.hash(value), true);
        }
    }

    //判断字符串是否已经被bits标记
    public boolean contains(String value) {
        if (value ==null) {
            returnfalse;
        }
        boolean ret =true;
        for (SimpleHash f : func) {
            ret = ret && bits.get(f.hash(value));
        }
        return ret;
    }

     /* 哈希函数类 */
     public static class SimpleHash {
        private int cap;
        private int seed;

        public SimpleHash(int cap, int seed) {
           this.cap = cap;
           this.seed = seed;
        }

        //hash函数,采用简单的加权和hash
        public int hash(String value) {
            int result =0;
            int len = value.length();
            for (int i =0; i < len; i++) {
                result = seed * result + value.charAt(i);
            }
            return (cap -1) & result;
        }
    }
}
    其python实现:
#!/usr/bin/python
#coding:utf-8

import cmath
from BitVector import BitVector

class BloomFilter(object):

    def __init__(self, errorRate, elementNum):
        #计算所需集合长度,公式见文末url
        self.bitNum = -1 * elementNum * cmath.log(errorRate) / (cmath.log(2.0) * cmath.log(2.0))
        #按四字节对齐
        self.bitNum = self.align4Byte(self.bitNum.real)
        #分配内存,生成类
        self.bitArray = BitVector(size=self.bitNum)
        #计算hash函数个数
        self.hashNum = cmath.log(2) * self.bitNum / elementNum

        self.hashNum = self.hashNum.real
        #取整
        self.hashNum = int(self.hashNum) + 1
        #产生hash函数种子
        self.hashSeeds = self.generateHashSeeds(self.hashNum)

    def insertElement(self, element):
        for seed in self.hashSeeds:
            hashVal = self.hashElement(element, seed)
            #取绝对值
            hashVal = abs(hashVal)
            #取模,防越界
            hashVal = hashVal % self.bitNum
            #设置相应的比特位
            self.bitArray[hashVal] = 1

    def isElementExist(self, element):
        for seed in self.hashSeeds:
            hashVal = self.hashElement(element, seed)
            #取绝对值
            hashVal = abs(hashVal)
            #取模,防越界
            hashVal = hashVal % self.bitNum
            #判断相应的比特位
            if self.bitArray[hashVal] == 0:
                return False
            return True

    def align4Byte(self, bitNum):
        num = int(bitNum / 32)
        num = 32 * (num + 1)
        return num

    def generateHashSeeds(self, hashNum):
        count = 0
        #连续两个种子的最小差值
        gap = 50
        #初始化hash种子为0
        hashSeeds = []
        for index in xrange(hashNum):
            hashSeeds.append(0)
        for index in xrange(10, 10000):
            maxNum = int(cmath.sqrt(1.0 * index).real)
            flag = 1
            for num in xrange(2, maxNum):
                if index % num == 0:
                    flag = 0
                    break

            if flag == 1:
                if count > 0 and (index - hashSeeds[count -1]) < gap:
                    continue
                hashSeeds[count] = index
                count = count + 1
            if count == hashNum:
                break
        return hashSeeds

    def hashElement(self, element, seed):
        hashVal = 1
        for ch in str(element):
            chval = ord(ch)
            hashVal = hashVal * seed + chval
        return hashVal



参考:http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html
c语言实现:http://www.cnblogs.com/haippy/archive/2012/07/14/2590669.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值