算法学习 - Bloom Filter(布隆过滤器)学习实现(C++实现)

非常感谢评论里指出了我代码里的小问题。以下代码修改了一下,主要是在第二次 HasH 的时候有小问题。

Bloom filter简介

Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员。如果检测结果为是,该元素不一定在集合中;但如果检测结果为否,该元素一定不在集合中。因此Bloom filter具有100%的召回率。这样每个检测请求返回有“在集合内(可能错误)”和“不在集合内(绝对不在集合内)”两种情况,可见 Bloom filter 是牺牲了正确率和时间以节省空间。

以上文字来源百度百科

Bloom Filter计算方法

如需要判断一个元素是不是在一个集合中,我们通常做法是把所有元素保存下来,然后通过比较知道它是不是在集合内,链表、树都是基于这种思路,当集合内元素个数的变大,我们需要的空间和时间都线性变大,检索速度也越来越慢。 Bloom filter 采用的是哈希函数的方法,将一个元素映射到一个 m 长度的阵列上的一个点,当这个点是 1 时,那么这个元素在集合内,反之则不在集合内。这个方法的缺点就是当检测的元素很多的时候可能有冲突,解决方法就是使用 k 个哈希 函数对应 k 个点,如果所有点都是 1 的话,那么元素在集合内,如果有 0 的话,元素则不在集合内。

Bloom Filter优点缺点

优点

  • 插入时间和查询时间都是常数。
  • 保存的不是数据本身,安全性好。

缺点

  • 插入的元素越多,错判性越大。
  • 不能删除元素。

图示说明

例如我们有一个简单的Bloom Filter结构如下:(所有位都是0)

      [ 0 0 0 0 0 0 0 0 0 0 ]

第一次插入a用两个哈希函数,映射到1 4位置上,变为1.

      [ 1 0 0 1 0 0 0 0 0 0 ]

第二次插入b同样的hash函数,映射到1 8位置上, 变为1.

      [ 1 0 0 1 0 0 0 1 0 0 ]

这样就存放了a b两个元素,当我们查找a是否在的时候,两次hash找到1 4位置,发现同时为 1。则表明a存在。

但是假如我们查找的d哈希后映射到4 8位置,发现也同时为 1. 认为存在,这就出错了,因为现在里面只存放了a b没有d

下面写下我用C++写的代码实现,比较简单的实现了下,具体的Hash算法我都简略的写了。

首先是头文件Header.h

//
//  Header.h
//  BloomFilter
//
//  Created by Alps on 15/3/19.
//  Copyright (c) 2015年 chen. All rights reserved.
//

#ifndef BloomFilter_Header_h
#define BloomFilter_Header_h

class BitMap{
public:
    BitMap(){
        bitmap = NULL;
        size = 0;
    }
    BitMap(int size){
        bitmap = NULL;
        bitmap = new char[size];
        if (bitmap == NULL) {
            printf("ErroR In BitMap Constractor!\n");
        }else{
            memset(bitmap, 0x0, size * sizeof(char));
            this->size = size;
        }
    }

    int initBitMap(int size){
        bitmap = NULL;
        bitmap = new char[size];
        if (bitmap == NULL) {
            printf("ErroR In BitMap Constractor!\n");
            return 0;
        }else{
            memset(bitmap, 0x0, size * sizeof(char));
            this->size = size;
            return this->size;
        }
    }


    /*
     * set the index bit to 1;
     */
    int bitmapSet(int index){
        int addr = index/8;
        int addroffset = index%8;
        unsigned char temp = 0x1 << addroffset;
        if (addr > (size+1)) {
            return 0;
        }else{
            bitmap[addr] |= temp;
            return 1;
        }
    }

    /*
     * return if the index in bitmap is 1;
     */
    int bitmapGet(int index){
        int addr = index/8;
        int addroffset = index%8;
        unsigned char temp = 0x1 << addroffset;
        if (addr > (size + 1)) {
            return 0;
        }else{
            return (bitmap[addr] & temp) > 0 ? 1 : 0;
        }
    }

    /*
     * del the index from 1 to 0
     */
    int bitmapDel(int index){
        if (bitmapGet(index) == 0) {
            return 0;
        }
        int addr = index/8;
        int addroffset = index%8;
        unsigned char temp = 0x1 << addroffset;
        if (addr > (size + 1)) {
            return 0;
        }else{
            bitmap[addr] ^= temp;
            return 1;
        }
    }

private:
    char *bitmap;
    int size;
};

#endif

头文件保存好,放到工程路径下。
下面是BloomFilter.cpp文件了。

//
//  main.cpp
//  BloomFilter
//
//  Created by Alps on 15/3/18.
//  Copyright (c) 2015年 chen. All rights reserved.
//

#include <iostream>
#include "Header.h"
using namespace std;


template <class Type> class BloomFilter{
public:
    BloomFilter();
    BloomFilter(int length){
        bitmap.initBitMap(length);
        this->length = length;
    }
    bool Add(const Type &T);
    bool Contains(const Type &T);
    int HasH(const Type &T);
    int SecondHasH(const Type &T);
private:
    BitMap bitmap;
    int length;
};
template <class Type> int BloomFilter<Type>::HasH(const Type &T){
    int temp = (int) T;
    return temp%length;
}

template <class Type> int BloomFilter<Type>::SecondHasH(const Type &T){
    int temp = (int) T;
    return temp%9973;
    //这里直接选了一个大素数 请各位自己优化自己的 HasH函数
    //不然会出现频繁的哈希冲突,并且把布隆过滤器的大小尽量放大一点几百万都可以
}

template <class Type> bool BloomFilter<Type>::Add(const Type &T){
    int first = HasH(T);
    int second = SecondHasH(first);
    if (bitmap.bitmapSet(first) && bitmap.bitmapSet(second)) {
        return true;
    }else{
        return false;
    }

}

template <class Type> bool BloomFilter<Type>::Contains(const Type &T){
    int first = HasH(T);
    int second =  SecondHasH(T);
    if (bitmap.bitmapGet(first) && bitmap.bitmapGet(second)) {
        return true;
    }else{
        return false;
    }
}




int main(int argc, const char * argv[]) {

    BloomFilter<int> bloom(10);

    bloom.Add(3);
    if (bloom.Contains(3)) {
        printf("true\n");
    }else{
        printf("false\n");
    }

    if (bloom.Contains(2)) {
        printf("true\n");
    }else{
        printf("false\n");
    }

    return 0;
}

这就是我的代码了。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
布隆过滤器是一种非常高效的数据结构,用于判断某个元素是否存在于一个集合中。它的基本原理是使用多个哈希函数对元素进行哈希,然后将哈希结果映射到一个位数组中的若干个位置,将这些位置标记为1。当需要查询某个元素是否在集合中时,将该元素进行哈希,判断哈希结果映射到的位数组上的值是否都为1,如果都为1,则说明该元素可能存在于集合中,但如果有任何一个位置为0,则说明该元素一定不存在于集合中。 布隆过滤器的应用场景非常广泛,例如网络爬虫中的URL去重、拼写检查、垃圾邮件过滤等。它的主要优点是占用空间非常小,而且查询速度非常快。 下面是一个简单的布隆过滤器实现示例: ```python import hashlib class BloomFilter: def __init__(self, m, k): self.m = m # 位数组的长度 self.k = k # 哈希函数的个数 self.bit_array = [0] * m # 初始化位数组 def add(self, key): for i in range(self.k): # 使用不同的哈希函数进行哈希 hash_val = int(hashlib.md5(str(key).encode('utf-8') + str(i).encode('utf-8')).hexdigest(), 16) # 将哈希结果映射到位数组上的若干个位置 pos = hash_val % self.m self.bit_array[pos] = 1 def contains(self, key): for i in range(self.k): hash_val = int(hashlib.md5(str(key).encode('utf-8') + str(i).encode('utf-8')).hexdigest(), 16) pos = hash_val % self.m if self.bit_array[pos] == 0: return False return True ``` 在上述代码中,我们使用了MD5哈希函数对字符串进行哈希,产生一个128位的哈希值,并将其转换为一个整数。然后,我们将这个整数对位数组的长度取模,得到一个在0到`m-1`之间的整数,将位数组上对应的位置标记为1。在查询元素是否存在于集合中时,我们同样对该元素进行k次哈希,并检查位数组上对应的位置是否都为1。如果有任何一个位置为0,则说明该元素一定不存在于集合中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值