【C++】BitSet和Bloom_Filter

简介: 位图(Bitmap)和布隆过滤器(Bloom Filter)是两种高效的数据结构。位图使用每一位二进制数表示数据项的存在状态,适用于精确判断元素存在性,广泛应用于图形图像处理、数据压缩、数据库索引等领域。布隆过滤器通过多个哈希函数将元素映射到位数组,用于快速判断元素是否可能属于集合,特别适合处理大规模数据,尽管存在误判率,但在网页缓存、网络数据包过滤等场景中表现出色。两者在空间效率、查询速度及误判率方面各有优势,适用于不同的应用场景。

前言:

在计算机图形学中,位图(Bitmap)也称为光栅图,是由像素点组成的图像表示方式。在 C++ 编程中,位图可以通过特定的函数和数据结构来进行处理和操作。

BitMap

  • 位图(BitMap)是一种数据结构,它使用每一位二进制数来表示一个数据项的存在状态。
  • 在C++中,位图通常用于处理大量的布尔值或整数集合,以实现高效的空间和时间性能。
  • 位图通过使用一组整数数组来表示一组元素的状态,其中每个整数可以表示多个元素的存在状态,通常是8个或更多。

BitMap常见操作

  • 位图提供了几种基本操作,包括设置(set)、重置(reset)和测试(test)。
  • 设置操作将指定的位设置为1,重置操作将指定的位设置为0,而测试操作则检查指定的位是否为1。
  • 这些操作通常涉及位运算符,如按位或(|=)、按位与(&=)和按位异或(^=)。

BitMap实现

  • 位图的精髓在于利于一个比特位确定一个值是否存在,更改的是比特位,就需要利用位操作符。
  • 给一个非类型模板参数进行空间的开辟。
  • 位图进行初始化,每个位置初始化为0,经过计算映射到对应的比特位。
  • 初始化除以一个32(以整形开辟的空间),+1是为了防止空间除后空间少32位。
  • setreset就是进行位图的核心操作。test的检测有很多方法,提供一个比较容易理解的检测。
template<size_t N >
class bitset
{
   
public:

    birset()
    {
   
        _a.resize(N / 32 + 1);
    }

    void set(const size_t x)
    {
   
        size_t i = x / 32;
        size_t j = x % 32;

        _a[i]|= (i<<j)
    }
    void reset(const size_t x)
    {
   
        size_t i = x / 32;
        size_t j = x % 32;

        _a[i] &= (~(i << 32));
    }

    bool test(const size_t x)
    {
   
        size_t i = x / 32;
        size_t j = x % 32;

        return  _a[i] & (1 << j);
    }
private:
    vector<int> _a;
};

BitMap应用

一、图形图像领域

  1. 图像编辑软件:在图像编辑软件中,位图是存储图像数据的主要方式。C++ 可以用于读取、处理和保存各种位图格式的图像,如 BMP、JPEG、PNG 等。通过对位图数据的操作,可以实现图像的裁剪、旋转、缩放、色彩调整等功能。例如,使用 C++ 和特定的图像库,可以加载一幅位图图像,对其进行亮度和对比度的调整,然后保存为新的图像文件。
  2. 游戏开发:在游戏开发中,位图用于存储游戏中的角色、场景、道具等图像资源。C++ 可以高效地处理位图图像,实现游戏中的各种图形效果。例如,在 2D 游戏中,可以使用位图来绘制游戏角色和背景;在 3D 游戏中,位图可以用于纹理映射,为 3D 模型添加真实感的表面纹理。
  3. 图形界面设计:在图形界面设计中,位图可以用于按钮、图标、背景等的显示。C++ 结合图形库可以实现丰富的图形界面效果。例如,使用 C++ 和 Qt 框架,可以加载位图图像作为按钮的图标,为用户界面增添美观性。

二、数据处理领域

  1. 数据可视化:在数据可视化中,位图可以用于将数据以图像的形式展示出来。C++ 可以处理大量的数据,并将其转换为位图图像进行可视化。例如,通过将数据点映射到位图的像素上,可以创建热力图、散点图等可视化图表。
  2. 数据库存储:在某些数据库系统中,位图索引是一种高效的数据存储和查询方式。C++ 可以用于实现位图索引的创建、维护和查询操作。例如,在处理大量的布尔型数据时,可以使用位图索引来快速查询满足特定条件的数据记录。
  3. 数据压缩:位图可以通过压缩算法来减少存储空间。C++ 可以实现各种位图压缩算法,如游程编码、霍夫曼编码等。例如,对于包含大量重复数据的位图图像,可以使用游程编码算法进行压缩,显著减少存储空间。

三、其他领域

  1. 医学影像处理:在医学影像处理中,位图用于存储 X 光片、CT 扫描、MRI 等图像数据。C++ 可以对医学位图图像进行处理和分析,帮助医生进行疾病诊断。例如,使用 C++ 和特定的医学影像处理库,可以对医学位图图像进行增强、分割、特征提取等操作,为医生提供更准确的诊断信息。
  2. 地理信息系统(GIS):在 GIS 中,位图可以用于存储地图数据、卫星图像等。C++ 可以处理和显示位图地图数据,实现地图的缩放、平移、查询等功能。例如,使用 C++ 和 GIS 库,可以加载位图地图图像,为用户提供地理信息查询和导航服务。
  3. 数字信号处理:在数字信号处理中,位图可以用于存储音频、视频等信号数据。C++ 可以对数字信号进行处理和分析,实现音频、视频的编码、解码、滤波等功能。例如,使用 C++ 和音频处理库,可以对音频信号进行滤波处理,去除噪声,提高音频质量。

布隆过滤器

布隆过滤器(Bloom Filter)作为一种高效的数据结构,在大规模数据处理中有着广泛的应用。尤其是在 C++ 语言环境下,其性能表现备受关注。然而,要确定 C++ 布隆过滤器在大规模数据处理中的性能极限并非易事,需要综合考虑多个因素。

  • 布隆过滤器是一种空间效率高的概率型数据结构,用于判断一个元素是否是某个集合的成员。
  • 它由布尔型数组、多个哈希函数和插入、查询操作组成。
  • 布隆过滤器的核心思想是通过多个哈希函数将输入元素映射到位数组的多个位置,并将这些位置设置为1。
  • 查询时,如果所有对应的位都为1,则元素可能存在;如果有任何一个位为0,则元素一定不存在.

位图和布隆过滤器区别

  1. 用哈希表存储用户记录,缺点:浪费空间。
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理 了。
  3. 将哈希与位图结合,即布隆过滤器。

布隆过滤器的实现

在C++中实现布隆过滤器,通常涉及以下步骤:

  • 定义std::bitset大小:根据预期的元素数量和允许的误判率,确定std::bitset的大小。
  • 选择哈希函数:布隆过滤器通常使用多个哈希函数来减少冲突。可以使用标准库中的哈希函数或自定义哈希函数。
  • 初始化std::bitset:创建一个std::bitset实例,其大小等于哈希函数的数量乘以位数组的大小。】
  • 插入元素:对于每个要插入的元素,使用每个哈希函数计算出多个位置,并将这些位置在std::bitset中设置为1。
  • 查询元素:要检查一个元素是否可能存在于布隆过滤器中,使用相同的哈希函数计算出该元素的位置,并检查这些位置是否全部为1。如果所有位置都为1,则元素可能存在;如果有任何一个位置为0,则元素一定不存在。
  • 优化和调整:根据实际应用中的性能和误判率要求,调整哈希函数的数量和std::bitset的大小。
  • 三个哈希函数具有非常良好算法性能,有效的减少哈希冲突。
struct BKDRHash
{
   
    size_t operator()(const string& s)
    {
   
        // BKDR
        size_t value = 0;
        for (auto ch : s)
        {
   
            value *= 31;
            value += ch;
        }
        return value;
    }
};
struct APHash
{
   
    size_t operator()(const string& s)
    {
   
        size_t hash = 0;
        for (long i = 0; i < s.size(); i++)
        {
   
            if ((i & 1) == 0)
            {
   
                hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
            }
            else
            {
   
                hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
            }
        }
        return hash;
    }
};
struct DJBHash {
   
    size_t operator()(const string& s)
    {
   
        size_t hash = 5381;
        for (auto ch : s)
        {
   
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};
  • 底层利用bitset不需要进行构造函数的实现

  • 过滤器通过映射3个位置减少哈希冲突。

class BloomFilter
{
   
public:
    void Set(const K& key)
    {
   
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        size_t index2 = HashFunc2()(key) % len;
        size_t index3 = HashFunc3()(key) % len;
        _bs.set(index1);
        _bs.set(index2);
        _bs.set(index3);
    }
    bool Test(const K& key)
    {
   
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        if (_bs.test(index1) == false)
            return false;
        size_t index2 = HashFunc2()(key) % len;
        if (_bs.test(index2) == false)
            return false;
        size_t index3 = HashFunc3()(key) % len;
        if (_bs.test(index3) == false)
            return false;
        return true;  // 存在误判的
    }
    // 不支持删除,删除可能会影响其他值。
    void Reset(const K& key);
private:
    bitset<X * N> _bs;
};

为什么布隆过滤器不支持元素的删除操作?

  • 布隆过滤器不支持元素删除操作的主要原因在于其工作原理。
  • 布隆过滤器通过多个哈希函数将元素映射到一个位数组中,并将相应位置设置为1来表示元素存在。
  • 由于哈希碰撞的可能性,一个位可能被多个元素共享。
  • 如果尝试删除一个元素,将其对应的位清零,可能会影响到其他元素,导致误判。因此,布隆过滤器的设计是“添加容易,删除困难”,以保持其高效的查询性能和低的空间占用。

Bitmap Bloom Filter区别

一、数据结构和存储方式

  1. 位图:
  • 数据结构:位图是由一系列比特位组成的数组,每个比特位对应一个特定的值或状态。
  • 存储方式:通过一位来表示一个特定的信息,例如,用 0 和 1 分别表示某个元素的存在与否。通常使用整数数组或位集等数据结构来实现。
  1. 布隆过滤器:
  • 数据结构:布隆过滤器由一个很长的二进制向量和多个哈希函数组成。
  • 存储方式:通过多个哈希函数将元素映射到二进制向量的多个位置,并将这些位置的值置为 1。查询时,通过相同的哈希函数检查这些位置是否都为 1,以此来判断元素是否可能存在于集合中。

    二、功能和用途

  1. 位图:
    • 主要功能:用于高效地表示和处理集合中的元素存在性问题,尤其适用于海量数据且数据无重复的场景。例如,可以快速判断某个整数是否在一个大的整数集合中。
    • 典型用途:在数据库索引、数据压缩、内存管理等领域有广泛应用。例如,在数据库中,可以用位图索引来快速判断某个记录是否满足特定条件。
  2. 布隆过滤器:
    • 主要功能:用于快速判断一个元素是否可能属于一个集合,具有较高的空间效率和查询速度,但存在一定的误判率。
    • 典型用途:在网页缓存、网络数据包过滤、分布式系统等领域中,用于快速过滤掉不可能存在的元素,减少不必要的计算和存储开销。例如,在网页缓存中,可以用布隆过滤器快速判断一个 URL 是否已经被缓存

三、性能特点

  1. 空间复杂度:

    • 位图:空间复杂度取决于要表示的元素数量和元素的取值范围。如果元素取值范围较小,位图的空间效率非常高。例如,对于表示 1000 个整数是否存在,只需要 1000 位(约 125 字节)的存储空间。
      • 布隆过滤器:空间复杂度相对较低,尤其是对于大规模数据集合。它的空间占用主要取决于预期元素数量和可接受的误判率。通过调整哈希函数的数量和二进制向量的大小,可以在一定程度上控制空间占用。
  2. 时间复杂度:

    • 位图:对于插入和查询操作,时间复杂度通常为 O (1),非常高效。因为只需要通过简单的位运算就可以完成操作。

    • 布隆过滤器:插入和查询操作的时间复杂度也非常低,接近 O (1)。但是,由于需要进行多次哈希函数计算,实际时间开销可能会略高于位图。

  3. 误判率:

    • 位图:如果正确实现和使用,位图不会产生误判。只要某个比特位被设置为 1,就表示对应的元素存在;如果为 0,则表示不存在。'

    • '布隆过滤器:存在一定的误判率,即可能会把不在集合中的元素误判为在集合中。误判率与哈希函数的数量、二进制向量的大小以及元素数量有关。可以通过调整这些参数来降低误判率,但同时也会增加空间占用。

四、适用场景

  • 当需要精确判断元素存在性且数据无重复时:适用数据结构:位图。
    1. 原因:位图可以准确地表示元素的存在与否,不会产生误判。如果数据没有重复,位图可以非常高效地利用存储空间,并且查询速度极快。
    2. 当需要快速判断元素可能存在性且可以接受一定误判率时:
      适用数据结构:布隆过滤器。
    3. 原因:布隆过滤器可以在非常低的空间开销下快速判断元素是否可能属于一个集合。虽然存在误判率,但在很多应用场景中,误判的影响可以通过其他方式进行处理或可以被接受。例如,在网页缓存中,即使偶尔误判一个 URL 已经被缓存过,也只是多进行了一次不必要的查询,不会对系统性能产生严重影响。

的存在与否,不会产生误判。如果数据没有重复,位图可以非常高效地利用存储空间,并且查询速度极快。

  1. 当需要快速判断元素可能存在性且可以接受一定误判率时:
    适用数据结构:布隆过滤器。
  2. 原因:布隆过滤器可以在非常低的空间开销下快速判断元素是否可能属于一个集合。虽然存在误判率,但在很多应用场景中,误判的影响可以通过其他方式进行处理或可以被接受。例如,在网页缓存中,即使偶尔误判一个 URL 已经被缓存过,也只是多进行了一次不必要的查询,不会对系统性能产生严重影响。
目录
相关文章
|
7月前
|
存储 人工智能 算法
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割(下)
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割
52 1
|
7月前
|
存储 算法 数据库
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割(中)
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割
62 1
|
7月前
|
存储 C语言 C++
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割(上)
从C语言到C++_32(哈希的应用)位图bitset+布隆过滤器+哈希切割
60 1
|
7月前
|
存储 算法 数据安全/隐私保护
C++ 位运算 std::bitset类的使用介绍
C++ 位运算 std::bitset类的使用介绍
225 0
(C++)STL之面向对象实验:小红花(运用bitset)
(C++)STL之面向对象实验:小红花(运用bitset)
|
C++ 存储 程序员
|
1月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
51 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
97 4