C++ 数据结构之布隆过滤器

yizhihongxing

C++ 数据结构之布隆过滤器

简介

布隆过滤器是一种用于快速判断一个元素是否存在于一个集合中的数据结构。它的空间效率和查询效率都比较高,在某些场景下,它可以代替传统的哈希表。

原理

布隆过滤器的基本原理是:将一个元素映射为多个位数组中的位置。在插入元素时,将这些位置上的值设置为1;在查询元素时,如果这些位置上的值都为1,则认为元素存在于集合中;否则认为元素不存在。(由于哈希函数的不可逆性,可能会出现把不同元素映射到同一位置的情况,因此判断元素不存在是有误差的,但判断元素存在则是准确的)。

为了使误判率尽可能小,布隆过滤器需要用到一个较大的位数组,以及多个不同的哈希函数。一般情况下,哈希函数使用 MurmurHash 或 xxHash 等快速哈希函数即可。

实现

下面介绍一个简单的布隆过滤器的实现。

代码如下:

#include <vector>
#include <cstdlib>
#include <ctime>

class BloomFilter {
public:
    BloomFilter(int n, double p): bit_array_(n), k_(std::log(2) * n / p) {
        srand(time(nullptr));
        for (int i = 0; i < k_; i++) {
            int random_seed = rand();
            hash_seeds_.push_back(random_seed);
        }
    }

    void Add(const std::string& str) {
        for (int i = 0; i < k_; i++) {
            int position = Hash(str, hash_seeds_[i]) % bit_array_.size();
            bit_array_[position] = true;
        }
    }

    bool Query(const std::string& str) const {
        for (int i = 0; i < k_; i++) {
            int position = Hash(str, hash_seeds_[i]) % bit_array_.size();
            if (!bit_array_[position]) {
                return false;
            }
        }
        return true;
    }

private:
    std::vector<bool> bit_array_;
    std::vector<int> hash_seeds_;
    int k_;

    int Hash(const std::string& str, int seed) const {
        unsigned int hash = seed;
        for (int i = 0; i < str.size(); i++) {
            hash = hash * 131 + str[i];
        }
        return hash;
    }
};

这段代码中,我们使用了一个 std::vector<bool> 类型的数组作为位数组,以及一个 std::vector<int> 类型的数组作为哈希种子。

在构造函数中,我们需要指定期望布隆过滤器的大小 n 和误判率 p。根据布隆过滤器的原理,一般情况下需要满足 k = ln(2) * n / p,其中 k 为哈希函数的个数。

随机生成 k 个哈希种子,用来生成 k 个哈希函数。

在插入元素时,通过 k 个哈希函数将元素映射到位数组中。将这些位置上的值设置为 1。

在查询元素时,同样使用 k 个哈希函数将元素映射到位数组中。如果所有位置上的值都为 1,则认为元素存在于集合中;否则认为元素不存在。

示例

下面给出两个使用布隆过滤器的示例。

示例1:URL去重

在爬虫程序中,我们需要对爬取到的每个URL进行去重操作。如果使用一个传统的哈希表来进行去重,需要使用大量的内存来存储这些URL,而且查询速度会较慢。布隆过滤器可以帮助我们在较小的空间和较快的查询速度之间取得平衡。

int main() {
    BloomFilter filter(1000000, 0.001);
    std::string url1 = "https://www.baidu.com";
    std::string url2 = "https://www.baidu.com";
    filter.Add(url1);
    if (filter.Query(url2)) {
        std::cout << "URL already exists." << std::endl;
    } else {
        std::cout << "URL does not exist." << std::endl;
    }
    return 0;
}

在上面的示例中,我们创建了一个布隆过滤器,指定了 1000000 的大小,以及 0.001 的误判率。然后向过滤器中插入了一个名字相同的 URL,并且查询了另一个名字相同的 URL。

示例2:IP黑名单过滤

在网络应用中,我们需要对一些IP地址进行黑名单过滤,防止这些IP地址发起攻击。布隆过滤器可以帮助我们在较小的内存和较快的查询速度之间取得平衡。

int main() {
    BloomFilter filter(1000000, 0.001);
    std::string ip1 = "192.168.0.1";
    std::string ip2 = "192.168.0.2";
    filter.Add(ip1);
    if (filter.Query(ip2)) {
        std::cout << "IP is in blacklist." << std::endl;
    } else {
        std::cout << "IP is not in blacklist." << std::endl;
    }
    return 0;
}

在上面的示例中,我们创建了一个布隆过滤器,指定了 1000000 的大小,以及 0.001 的误判率。然后向过滤器中插入了一个 IP 地址,并且查询了另一个 IP 地址,看它是否在黑名单中。

总结

布隆过滤器是一种空间效率和查询效率都比较高的数据结构,常用于集合判重、黑名单过滤等场景中。使用布隆过滤器需要注意一些参数的设置,以及哈希函数的选择。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ 数据结构之布隆过滤器 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 数据结构中的各种排序方法小结(JS实现)

    数据结构中的各种排序方法小结(JS实现) 本文将介绍常见的八种排序算法: 冒泡排序 插入排序 选择排序 快速排序 归并排序 堆排序 希尔排序 计数排序 下面进行详细讲解。 冒泡排序 冒泡排序是一种简单的排序算法,它重复地遍历数组,比较相邻的两个元素,并按大小交换位置,一直到整个数组排序完成。它的时间复杂度为O(n^2)。 示例代码: function bub…

    数据结构 2023年5月17日
    00
  • JavaScript 处理树数据结构的方法示例

    下面是“JavaScript 处理树数据结构的方法示例”的完整攻略。 什么是树数据结构 树形数据结构是一种非常重要的数据结构,常被用于模拟现实中大量的层级结构。例如:文件目录、网站导航等。其是由一个根节点和若干个子节点构成的,每个节点可以有0个或多个子节点。 使用 JavaScript 处理树形数据结构 了解了树形数据结构后,我们可以使用 JavaScrip…

    数据结构 2023年5月17日
    00
  • C语言数据结构旋转链表的实现

    C语言数据结构旋转链表的实现 1. 什么是旋转链表 旋转链表是一种特殊的链表,其特点是将链表的最后一个节点移动到最前面,形成一个环形链表的效果。比如下面这个链表: 1 -> 2 -> 3 -> 4 -> 5 经过一次旋转之后变成了: 5 -> 1 -> 2 -> 3 -> 4 2. 实现思路 旋转链表的实现思路…

    数据结构 2023年5月17日
    00
  • C++ 超详细分析数据结构中的时间复杂度

    C++ 超详细分析数据结构中的时间复杂度攻略 什么是时间复杂度? 时间复杂度是用来衡量算法效率的指标,它表示的是算法的执行时间与问题规模之间的关系,通常用大O记法来表示。 如何分析时间复杂度? 1. 常见时间复杂度 以下是常见的时间复杂度及其对应的执行次数: 时间复杂度 对应执行次数 O(1) 常数级别 O(log n) 对数级别 O(n) 线性级别 O(n…

    数据结构 2023年5月17日
    00
  • C++数据结构之链表详解

    C++数据结构之链表详解 链表是一种重要的数据结构,它可以动态的分配内存空间,实现灵活的元素插入,删除等操作。本文将详细讲解链表的定义、实现、常见操作以及链表的应用。 定义与特点 链表是一种线性表数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表分为单向链表和双向链表,其中单向链表的每个节点只包含一个指针,指向下一个节点,而双向链表的每…

    数据结构 2023年5月17日
    00
  • Java数据结构之哈夫曼树概述及实现

    Java数据结构之哈夫曼树概述及实现 哈夫曼树概述 哈夫曼树(Huffman Tree),也称为最优树(Optimal Binary Tree),是一种带权路径长度最短的二叉树,也就是最小权重的前缀编码树。其基本思想是采用频率作为节点的权值,将频率较小的节点放在左子树上,频率较大的节点放在右子树上,从而形成一棵权值最小的二叉树。 实现过程 实现哈夫曼树需要以…

    数据结构 2023年5月17日
    00
  • TypeScript数据结构栈结构Stack教程示例

    下面就给您详细讲解一下“TypeScript数据结构栈结构Stack教程示例”的完整攻略。 1. 栈结构(Stack)概述 栈是一种特殊的数据结构,它的特点是后进先出(Last In First Out,LIFO)。和数组不同的是,栈只能在栈顶插入和删除元素。栈的常见操作有“- push() 元素入栈,将元素放到栈顶- pop() 元素出栈,从栈顶取出元素…

    数据结构 2023年5月17日
    00
  • 李航统计学习概述

    监督学习 感知机 概念: 感知机模型的基本形式是: \(f(x) = sign(w \cdot x + b)\) 其中,\(x\) 是输入样本的特征向量,\(w\) 是权值向量,\(b\) 是偏置量,\(w \cdot x\) 表示向量 \(w\) 和 \(x\) 的点积。\(sign\) 函数表示符号函数,当输入大于 0 时输出 1,否则输出 -1。 要求…

    算法与数据结构 2023年4月25日
    00
合作推广
合作推广
分享本页
返回顶部