哈希扩展--位图和布隆过滤器

本文介绍位图数据结构的原理及应用,并基于位图实现布隆过滤器,用于高效判断元素是否存在。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

位图

  • 位图就是用一个比特位来表示两种状态,0或者1;这样做可以大大的减少空间的使用节约空间,一个整形数据在32位机中为32个字节,用一个整形数据就可以表示32个“事件”的状态,适用于大规模数据,但数据状态又不是很多的情况,通常是用来判断某个数据存不存在的。

代码实现如下:

BitMap.h

#ifndef __BITMAP_H__
#define __BITMAP_H__

#include <stdio.h>
#include <malloc.h>
#include <assert.h>


typedef struct BitMap
{
    int* _bmp;
    int _capacity; //位集合所占空间大小(有几个整形数据)
    int _size;    //比特位的有效个数(假如最大数为24,他的有效个数就为25,0到24有25个数)
}BMap;




void BitMapInit(BMap* bp, int bitcount);

void BitMapSet(BMap* bp, int which);

//将任意一位置零
void BitMapReSet(BMap* bp, int which);

//判断任意一位是0或1
int BitMapTest(BMap* bp, int which);

int BitMapSize(BMap* bp);

//比特位为1的总个数
int BitMapCount(BMap* bp);

//销毁位图
void BitMapDestroy(BMap* bp);


#endif

BitMap.c

#include "BitMap.h"

//初始化
void BitMapInit(BMap* bp, int bitcount)//第二个参数为需要比特位的个数
{
    assert(bp);
    bp->_capacity = bitcount / 32 + 1;

    //因为需要每一个比特位初始化都为0,所以这里用calloc
    bp->_bmp = (int*)calloc(bp->_capacity, sizeof(int));
    if (NULL == bp->_bmp)
    {
        assert(0);
        return;
    }

    bp->_size = bitcount;
}

//将任意一位置1
void BitMapSet(BMap* bp, int which)//which从0开始
{
    int index = 0;
    int pos = 0;

    assert(bp);
    if (which >= bp->_size)
        return;

    index = which / 32;//计算出在哪一个整形数据中
    pos = which % 32;  //计算出再这个数的哪一位

    bp->_bmp[index] |= (1 << pos);
}

//将任意一位置零
void BitMapReSet(BMap* bp, int which)
{
    int index = 0;
    int pos = 0;
    assert(bp);

    if (which >= bp->_size)
        return;

    index = index = which / 32;
    pos = which % 32;  
    bp->_bmp[index] &= ~(1 << pos);
}

//判断任意一位是0或1
int BitMapTest(BMap* bp, int which)
{
    int index = 0;
    int pos = 0;
    assert(bp);

    if (which >= bp->_size)
    {
        printf("which超过表示范围!\n");
        return 0;
    }

    index = which / 32;
    pos = which % 32;

    return bp->_bmp[index] & (1 << pos);
}

int BitMapSize(BMap* bp)
{
    assert(bp);

    return bp->_size;
}

//比特位为1的总个数
int BitMapCount(BMap* bp)
{
    int i = 0;
    int count = 0;
    const char bitCount[] = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4";//'\'相当于逗号,将0-15一的个数直接写在表中

    for (; i < bp->_capacity; i++)
    {
        int tmp = bp->_bmp[i];
        int j = 0;

        //每一次循环只能算一个字节中比特位的个数
        while (j < sizeof(bp->_bmp[0]))
        {
            char c = tmp;
            count += bitCount[c & 0x0f];//每次只能算四个比特位中1的个数

            c >>= 4;
            count += bitCount[c & 0x0f];


            tmp >>= 8;//算第二个字节
            j++;
        }
    }

    return count;
}

//销毁位图
void BitMapDestroy(BMap* bp)
{
    assert(bp);
    free(bp->_bmp);
    bp->_bmp = NULL;
    bp->_capacity = 0;
    bp->_size = 0;
}

test.c

#include "BitMap.h"
#include "BloomFilter.h"

void TestBitMap()
{
    BMap bp;
    BitMapInit(&bp, 28);
    BitMapSet(&bp, 3);//which是从0开始
    BitMapSet(&bp, 4);
    BitMapSet(&bp, 1);
    BitMapReSet(&bp, 1);

    printf("第零位为:%d\n", BitMapTest(&bp, 0));
    printf("第一位为:%d\n", BitMapTest(&bp, 1));

    printf("位图中共有1:%d个\n", BitMapCount(&bp));
    BitMapDestroy(&bp);
}

int main()
{
    TestBitMap();
}

布隆过滤器

  • 布隆过滤器就是用于海量数据中查找某个元素在不在,如:一个单词是否在已知的词典当中;一个嫌疑人的名字是否已经在嫌疑人名单当中;这些数据的特点就是占用空间大,如果直接用哈希表的话,不好处理,这样我们就很容易想到用上面的位图来解决这些问题,但是,数据,该怎么放呢?我们可以采用哈希函数,来将数据映射到对应的比特位,取的时候用同样的方法,但是,又存在一个问题,那就是哈希冲突,解决哈希冲突的办法就是使用多个哈希函数来映射,如果他们说有一个元素不在集合,那么他就肯定不在。如果他们都说在,虽然有可能不在,但直觉上判断这种概率相对较低。

代码实现如下:

BloomFilter.h

#pragma once


#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include "BitMap.h"
#include "comm.h"


typedef char* DataType;
typedef unsigned(*PSTI)(DataType str);


typedef struct BloomFilter
{
    BMap _bp;
    int _size;  //有效数据的个数
    PSTI _STI[5]; //函数指针数组
}BFilter;

//初始化
void BloomFilterInit(BFilter* bf, int capacity, PSTI* pSTI, int size);//size表示数组的大小

//插入元素
void BloomFilterInseret(BFilter* bf, DataType data);

//判断数据是否在布隆中--只要算出来一个不在就肯定不在
int BloomFilterIsIn(BFilter* bf, DataType data);

int BloomFilterSize(BFilter* bf);

void BloomFilterDestroy(BFilter* bf);

BloomFilter.c

#include "BloomFilter.h"
#include "BitMap.h"
#include "comm.h"

//初始化
void BloomFilterInit(BFilter* bf, int capacity, PSTI* pSTI, int size)//size表示数组的大小
{
    int i = 0;
    assert(bf);
    BitMapInit(&bf->_bp, capacity * 5);//
    bf->_size = 0;

    for (; i < size; i++)
        bf->_STI[i] = pSTI[i];
}


//插入元素
void BloomFilterInseret(BFilter* bf, DataType data)
{
    assert(bf);
    unsigned hashaddr = -1;

    hashaddr = bf->_STI[0](data) % bf->_bp._size;
    BitMapSet(&bf->_bp, hashaddr);

    hashaddr = bf->_STI[1](data) % bf->_bp._size;
    BitMapSet(&bf->_bp, hashaddr);

    hashaddr = bf->_STI[2](data) % bf->_bp._size;
    BitMapSet(&bf->_bp, hashaddr);

    hashaddr = bf->_STI[3](data) % bf->_bp._size;
    BitMapSet(&bf->_bp, hashaddr);

    hashaddr = bf->_STI[4](data) % bf->_bp._size;
    BitMapSet(&bf->_bp, hashaddr);

    bf->_size++;
}

//判断数据是否在布隆中--只要算出来一个不在就肯定不在
int BloomFilterIsIn(BFilter* bf, DataType data)
{
    assert(bf);
    unsigned hashaddr = -1;

    hashaddr = bf->_STI[0](data) % bf->_bp._size;
    if (!BitMapTest(&bf->_bp, hashaddr))
        return 0;

    hashaddr = bf->_STI[1](data) % bf->_bp._size;
    if (!BitMapTest(&bf->_bp, hashaddr))
        return 0;

    hashaddr = bf->_STI[2](data) % bf->_bp._size;
    if (!BitMapTest(&bf->_bp, hashaddr))
        return 0;

    hashaddr = bf->_STI[3](data) % bf->_bp._size;
    if (!BitMapTest(&bf->_bp, hashaddr))
        return 0;

    hashaddr = bf->_STI[4](data) % bf->_bp._size;
    if (!BitMapTest(&bf->_bp, hashaddr))
        return 0;

    return 1;
}


int BloomFilterSize(BFilter* bf)
{
    assert(bf);

    return bf->_size;
}


void BloomFilterDestroy(BFilter* bf)
{
    assert(bf);

    BitMapDestroy(&bf->_bp);
    bf->_size = 0;
}

comm.h

#pragma once

//将字符串转化为整数
unsigned StrToInt1(const char * str);

unsigned int StrToInt2(char *str);

// RS Hash Function
unsigned int StrToInt3(char *str);

// JS Hash Function
unsigned int StrToInt4(char *str);

// ELF Hash Function
unsigned int StrToInt5(char *str);

unsigned IntToInt(int data);

comm.c

#include "comm.h"



//将字符串转换为整数
unsigned StrToInt1(const char * str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}

unsigned IntToInt(int data)
{
    return data;
}

unsigned int StrToInt2(char *str)
{
    unsigned int hash = 0;

    while (*str)
    {
        // equivalent to: hash = 65599*hash + (*str++);
        hash = (*str++) + (hash << 6) + (hash << 16) - hash;
    }

    return (hash & 0x7FFFFFFF);
}

// RS Hash Function
unsigned int StrToInt3(char *str)
{
    unsigned int b = 378551;
    unsigned int a = 63689;
    unsigned int hash = 0;

    while (*str)
    {
        hash = hash * a + (*str++);
        a *= b;
    }

    return (hash & 0x7FFFFFFF);
}

// JS Hash Function
unsigned int StrToInt4(char *str)
{
    unsigned int hash = 1315423911;

    while (*str)
    {
        hash ^= ((hash << 5) + (*str++) + (hash >> 2));
    }

    return (hash & 0x7FFFFFFF);
}

// ELF Hash Function
unsigned int StrToInt5(char *str)
{
    unsigned int hash = 0;
    unsigned int x = 0;

    while (*str)
    {
        hash = (hash << 4) + (*str++);
        if ((x = hash & 0xF0000000L) != 0)
        {
            hash ^= (x >> 24);
            hash &= ~x;
        }
    }

    return (hash & 0x7FFFFFFF);
}
<think>好的,用户想了解C++中位图布隆过滤器的相关知识及总结,我需要结合提供的引用资料来组织回答。首先,我应该先明确位图布隆过滤器的基本概念,然后讨论它们的实现应用,最后总结它们的优缺点应用场景。 首先看位图部分。根据引用[3],位图用于处理海量数据的存在性判断,比如40亿个整数去重。位图通过每个bit位表示一个数据是否存在,节省空间。需要提到位图的数据结构设计,比如用vector<bool>或直接操作比特位。实现部分可能需要给出C++的代码示例,比如设置某位为1,清除为0,检查存在性。应用方面包括快速查找、去重、排序等,引用[2][3]都有提到这些应用。 然后是布隆过滤器。根据引用[4],布隆过滤器是概率型数据结构,用于判断元素可能存在或一定不存在。它使用多个哈希函数映射到不同的位,引用[3][4]都提到了这一点。实现部分需要说明插入查找的过程,以及为什么不能删除,或者如何支持删除(可能需要引用计数)。应用方面,引用[5]提到了缓存穿透的解决方案,防止数据库被无效查询压垮。此外,分布式系统爬虫中的URL去重也是常见应用。 需要注意布隆过滤器的优缺点,比如空间效率高但存在误判率,无法删除元素等。引用[4][5]都有相关描述。可能需要比较位图布隆过滤器的适用场景,比如位图适合精确判断且数据范围小的情况,而布隆过滤器适合大数据量且允许一定误判的情况。 代码实现部分,位图可以用bitset或手动操作位,布隆过滤器则需要多个哈希函数位图的组合。需要确保示例代码正确,比如展示如何设置位,检查存在性,以及布隆过滤器的插入查询步骤。 最后总结部分,需要将位图布隆过滤器的核心点对比,强调它们在海量数据处理中的重要性,并结合引用中的内容,如引用[1]提到的结合位图布隆过滤器的方案,可能可以提到这种混合结构在优化空间查询速度上的优势。 现在需要检查是否有遗漏的引用内容,比如引用[1]提到结合位图布隆过滤器的优点,这可能是在实际应用中的优化策略,需要在总结部分提到。同时,用户要求生成相关问题,需要根据内容设计相关问题,比如应用场景、实现细节、优缺点比较等。 需要注意回答的结构,先讲位图,再讲布隆过滤器,每个部分包括概念、实现、应用,最后总结对比。确保引用标识正确添加,例如在提到布隆过滤器概念时加上[^4],在缓存穿透时引用[^5]等。</think>### C++位图布隆过滤器总结 #### 一、位图(Bitmap) **1. 概念与实现** 位图通过每个比特位(bit)表示一个数据的存在性(0/1),适用于海量数据的快速存在性判断[^3]。例如,40亿个整数(约16GB)用位图仅需约500MB内存。 **C++实现示例**: ```cpp class Bitmap { private: std::vector<uint32_t> bits; // 使用uint32_t数组存储位 public: Bitmap(size_t range) : bits((range >> 5) + 1, 0) {} // 计算需要多少32位块 void set(size_t x) { size_t index = x >> 5; // 确定数组下标 size_t offset = x % 32; // 确定比特位偏移 bits[index] |= (1 << offset); } bool test(size_t x) { size_t index = x >> 5; size_t offset = x % 32; return (bits[index] & (1 << offset)) != 0; } }; ``` **2. 应用场景** - **数据去重**:如统计40亿整数中不重复的数字。 - **快速查询**:判断IP是否在黑名单中。 - **排序**:对有限范围的整数进行非比较排序[^2]。 --- #### 二、布隆过滤器(Bloom Filter) **1. 概念与实现** 布隆过滤器通过$k$个哈希函数将元素映射到多个位,若所有对应位为1则“可能存在”,否则“一定不存在”[^4]。牺牲一定准确性换取极高的空间效率。 **C++实现核心逻辑**: ```cpp class BloomFilter { private: Bitmap bitmap; size_t k; // 哈希函数数量 public: BloomFilter(size_t size, size_t k) : bitmap(size), k(k) {} void add(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); bitmap.set(hash % bitmap.size()); } } bool contains(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); if (!bitmap.test(hash % bitmap.size())) return false; } return true; // 可能存在(有一定误判率) } }; ``` **2. 应用场景** - **缓存穿透防护**:拦截不存在的数据请求,保护数据库。 - **分布式系统**:减少节点间冗余数据传输。 - **爬虫URL去重**:避免重复抓取已处理的页面[^3]。 **3. 优缺点** - **优点**:空间效率高,查询时间$O(k)$,适合海量数据[^4]。 - **缺点**:误判率随元素增加而上升,且不支持删除(除非引入计数布隆过滤器)。 --- #### 三、对比与总结 | **特性** | **位图** | **布隆过滤器** | |------------------|------------------------------|------------------------------| | **数据范围** | 适用于整数且范围较小 | 支持任意数据类型 | | **误判率** | 无 | 有(可调整哈希函数数量优化) | | **删除支持** | 直接修改位即可 | 需额外结构(如计数位图) | | **典型场景** | 精确存在性判断 | 允许误判的存在性预筛 | **混合优化方案**:结合位图布隆过滤器,例如用位图处理高频数据,布隆过滤器处理低频数据,提升整体性能[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值