【算法系列 | 7】深入解析查找算法之—布隆过滤器

这篇具有很好参考价值的文章主要介绍了【算法系列 | 7】深入解析查找算法之—布隆过滤器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【算法系列 | 7】深入解析查找算法之—布隆过滤器,算法系列,赠书活动,算法,数据结构,布隆过滤,原力计划

序言

心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。

决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。

我们一起努力,成为更好的自己!

今天第3讲,讲一下排序算法的选择排序(Selection Sort)

1 基础介绍

查找算法是很常见的一类问题,主要是将一组数据按照某种规则进行排序。

以下是一些常见的查找算法及其应用场景:

  1. 布隆过滤器(Bloom Filter):适用于判断一个元素是否存在于一个大规模的数据集中,时间复杂度为O(1),但有一定的误判率。
  2. 二分查找(Binary Search):适用于有序数组中查找元素,时间复杂度为O(log n);
  3. 哈希表查找(Hash Table:适用于快速查找和插入元素,时间复杂度为O(1),但需要额外的存储空间;
  4. 线性查找(Linear Search):适用于无序数组中查找元素,时间复杂度为O(n);
  5. 插值查找(Interpolation Search):适用于有序数组中查找元素,时间复杂度为O(log log n),但是对于分布不均匀的数据集效果不佳;
  6. 斐波那契查找(Fibonacci Search):适用于有序数组中查找元素,时间复杂度为O(log n),但需要额外的存储空间;
  7. 树表查找(Tree Search):适用于快速查找和插入元素,时间复杂度为O(log n),但需要额外的存储空间;
  8. B树查找(B-Tree):适用于大规模数据存储和查找,时间复杂度为O(log n),但需要额外的存储空间;

一、布隆过滤器介绍

1.1 原理介绍

布隆过滤器(Bloom Filter)是一种概率型数据结构,用于快速判断一个元素是否可能存在于一个集合中,同时具有高效的插入和查询操作。它的原理基于位数组和哈希函数。

布隆过滤器的核心是一个位数组(bit array)或称为位向量(bit vector),用于表示元素的存在状态。初始时,所有位都被置为0。

布隆过滤器使用一系列不同的哈希函数(hash functions),这些哈希函数将输入的元素映射为位数组中的不同位置。哈希函数的数量和定义根据具体的应用场景而定,通常会选择一些独立的哈希函数。

插入元素时,将元素经过哈希函数的映射,得到一系列位数组中的位置,然后将这些位置的位设置为1,表示对应的元素存在于布隆过滤器中。

查询元素时,将要查询的元素经过哈希函数的映射,得到一系列位数组中的位置,然后检查这些位置的位是否都为1。

如果有任何一个位置的位为0,则可以确定该元素一定不存在于布隆过滤器中;如果所有位置的位都为1,则该元素可能存在于布隆过滤器中,但不是确定存在。

因此,布隆过滤器的查询结果可能会有误判,即将不存在的元素误判为存在,但不会将存在的元素误判为不存在。

优点

由于布隆过滤器使用位数组和哈希函数,具有以下特点:

  1. 空间效率高:布隆过滤器只需使用位数组存储元素的存在状态,不需要存储元素本身,因此占用的空间相对较小。

  2. 查询效率高:布隆过滤器查询的时间复杂度是常数级别的,与集合中元素的数量无关。

  3. 插入效率高:布隆过滤器插入的时间复杂度也是常数级别的。

缺点

然而,布隆过滤器也存在一些缺点:

  1. 误判率(False Positive):布隆过滤器的查询结果可能会有误判,将不存在的元素误判为存在。

  2. 不支持元素的删除:布隆过滤器的设计目标是快速判断元素的存在性,不支持元素的删除操作。

  3. 难以扩展:一旦布隆过滤器被创建,就很难动态地调整其大小。

总的来说,布隆过滤器适用于对查询效率要求较高、可以容忍一定的误判率以及元素不经常变动的场景,如缓存、防止缓存击穿、爬虫的去重等应用。

图解原理

以下是一个简单的图解布隆过滤器的示意图:

图表示一个初始状态的布隆过滤器,位数组中的所有位都被初始化为0。

【算法系列 | 7】深入解析查找算法之—布隆过滤器,算法系列,赠书活动,算法,数据结构,布隆过滤,原力计划

接下来,我们插入一个元素 "apple" 和 "orange",假设我们选择了三个哈希函数,并且得到的哈希值分别为 1、5 和 7。我们将对应的位设置为1,表示这些位置上的元素存在。

【算法系列 | 7】深入解析查找算法之—布隆过滤器,算法系列,赠书活动,算法,数据结构,布隆过滤,原力计划

 现在,我们查询元素 "apple" 和 "banana"。根据哈希函数得到的位位置分别为 1、4 和 7。我们检查这些位置上的位,如果其中有任何一个位为0,则可以确定该元素不存在于布隆过滤器中;如果所有位置上的位都为1,则该元素可能存在于布隆过滤器中。

【算法系列 | 7】深入解析查找算法之—布隆过滤器,算法系列,赠书活动,算法,数据结构,布隆过滤,原力计划

根据图示,我们可以确定 "apple" 可能存在于布隆过滤器中,因为对应的位置上的位都为1。而 "banana" 可能不存在于布隆过滤器中,因为其中一个位置上的位为0。

这就是布隆过滤器的基本原理。

通过使用位数组和哈希函数,布隆过滤器可以快速判断一个元素是否可能存在于一个集合中,具有高效的插入和查询操作。

1.2 复杂度 

布隆过滤器的时间和空间复杂度如下:

时间复杂度:

  • 插入操作的时间复杂度是O(k),其中k是哈希函数的数量。
  • 查询操作的时间复杂度也是O(k)。

空间复杂度:

  • 布隆过滤器的空间复杂度主要取决于位数组的大小和哈希函数的数量。通常情况下,位数组的大小取决于预期的元素数量和期望的误判率。位数组的大小会随着元素数量的增加而增加,以及期望的误判率的降低而增加。

1.3使用场景

布隆过滤器适用于以下场景:

  1. 数据量大,但内存有限:由于布隆过滤器只需要使用位数组来表示元素存在状态,相对于其他数据结构,它具有较小的内存占用。这使得它在内存有限的情况下能够存储大量的元素。

  2. 快速判断元素是否存在:布隆过滤器可以在常数时间内判断一个元素是否可能存在于集合中,无需实际存储元素本身,这使得它具有非常高的查询效率。它可以用于加速对大型数据集或数据库的查询操作。

  3. 容忍一定的误判率:布隆过滤器的查询结果可能会有误判,将不存在的元素误判为存在(即假阳性)。因此,它适用于那些可以容忍一定误判率的应用场景。例如,网页爬虫可以使用布隆过滤器来去重,避免重复爬取相同的网页;缓存系统可以使用布隆过滤器来判断某个数据是否已经缓存,从而避免无谓的IO操作。

  4. 不需要删除操作:布隆过滤器不支持元素的删除操作。一旦元素被插入到布隆过滤器中,就无法删除。因此,它适用于那些不需要频繁删除元素的场景。

需要注意的是,布隆过滤器在某些情况下可能会出现误判,将不存在的元素误判为存在。因此,在一些对准确性要求很高的场景下,布隆过滤器可能不适用。

二、代码实现

2.1 Python 实现

代码示例

import math
import mmh3
from bitarray import bitarray

class BloomFilter:
    def __init__(self, num_items, false_positive_rate):
        self.num_items = num_items
        self.false_positive_rate = false_positive_rate
        self.bit_array_size = self.calculate_bit_array_size(num_items, false_positive_rate)
        self.num_hash_functions = self.calculate_num_hash_functions(self.bit_array_size, num_items)
        self.bit_array = bitarray(self.bit_array_size)
        self.bit_array.setall(0)

    def calculate_bit_array_size(self, num_items, false_positive_rate):
        numerator = num_items * math.log(false_positive_rate)
        denominator = math.log(2) ** 2
        return int(-(numerator / denominator))

    def calculate_num_hash_functions(self, bit_array_size, num_items):
        numerator = (bit_array_size / num_items) * math.log(2)
        return int(numerator)

    def add(self, item):
        for seed in range(self.num_hash_functions):
            index = mmh3.hash(item, seed) % self.bit_array_size
            self.bit_array[index] = 1

    def contains(self, item):
        for seed in range(self.num_hash_functions):
            index = mmh3.hash(item, seed) % self.bit_array_size
            if self.bit_array[index] == 0:
                return False
        return True

代码讲解

现在逐行解释代码的各个部分:

  1. 导入所需的模块:代码使用了 math 模块来进行数学计算,mmh3 模块用于实现哈希函数,bitarray 模块用于表示位数组。

  2. BloomFilter 类的初始化方法:在初始化过程中,我们需要指定预期的元素数量 num_items 和期望的误判率 false_positive_rate。根据这两个参数,我们通过调用 calculate_bit_array_size 和 calculate_num_hash_functions 方法来计算位数组的大小和哈希函数的数量。然后,我们创建一个位数组 bit_array,并将所有位初始化为0。

  3. calculate_bit_array_size 方法:根据预期元素数量和期望的误判率,使用公式 -num_items * log(false_positive_rate) / (log(2) ** 2) 计算位数组的大小,并将结果转换为整数。

  4. calculate_num_hash_functions 方法:根据位数组的大小和预期元素数量,使用公式 (bit_array_size / num_items) * log(2) 计算哈希函数的数量,并将结果转换为整数。

  5. add 方法:用于向布隆过滤器中添加元素。对于每个元素,我们使用不同的种子值(从0到num_hash_functions-1)来计算哈希值,并将对应的位数组位置设置为1。

  6. contains 方法:用于检查元素是否存在于布隆过滤器中。对于每个元素,我们使用与添加操作相同的种子值来计算哈希值,并检查对应的位数组位置。如果任何一个位置上的位为0,则可以确定元素不存在于布隆过滤器中;否则,我们认为元素可能存在于布隆过滤器中。

这就是一个简单的布隆过滤器的 Python 实现。你可以根据自己的需求进行调整和扩展。请注意,这个实现中并没有考虑动态调整布隆过滤器大小或删除元素的功能。

测试代码

import random

def generate_random_string(length):
    letters = "abcdefghijklmnopqrstuvwxyz"
    return ''.join(random.choice(letters) for _ in range(length))

num_items = 1000
false_positive_rate = 0.01

bloom_filter = BloomFilter(num_items, false_positive_rate)

# 添加元素
for _ in range(num_items):
    item = generate_random_string(10)
    bloom_filter.add(item)

# 检查元素是否存在
positive_count = 0
for _ in range(1000):
    item = generate_random_string(10)
    if bloom_filter.contains(item):
        positive_count += 1

false_positive_rate_actual = positive_count / 1000
print("实际误判率:", false_positive_rate_actual)

我们首先定义了预期的元素数量 num_items 和期望的误判率 false_positive_rate。然后,我们创建了一个 BloomFilter 实例并使用 add 方法向布隆过滤器中添加了随机生成的元素。

接下来,我们使用 contains 方法进行随机测试。我们随机生成了1000个字符串,并检查它们是否存在于布隆过滤器中。我们计算了实际的误判率,即在这1000个随机字符串中错误判断为存在的比例,并将其打印出来。

测试结果会输出一个实际的误判率,应该接近于设定的期望误判率。

请注意,由于布隆过滤器的特性,即使在没有添加的情况下,也可能会有一定的误判率。因此,实际误判率可能略高于设定的期望误判率。

运行结果

实际误判率: 0.009

2.2Java实现

代码示例

import java.util.BitSet;
import java.util.Random;

public class BloomFilter {
    private BitSet bitSet;
    private int size;
    private int numHashFunctions;
    private Random random;

    public BloomFilter(int expectedNumItems, double falsePositiveRate) {
        size = calculateBitSetSize(expectedNumItems, falsePositiveRate);
        numHashFunctions = calculateNumHashFunctions(size, expectedNumItems);
        bitSet = new BitSet(size);
        random = new Random();
    }

    public void add(String item) {
        for (int i = 0; i < numHashFunctions; i++) {
            int hash = hash(item, i);
            bitSet.set(hash, true);
        }
    }

    public boolean contains(String item) {
        for (int i = 0; i < numHashFunctions; i++) {
            int hash = hash(item, i);
            if (!bitSet.get(hash)) {
                return false;
            }
        }
        return true;
    }

    private int hash(String item, int seed) {
        random.setSeed(seed);
        return Math.abs(random.nextInt()) % size;
    }

    private int calculateBitSetSize(int expectedNumItems, double falsePositiveRate) {
        int size = (int) Math.ceil((expectedNumItems * Math.log(falsePositiveRate)) / Math.log(1.0 / (Math.pow(2.0, Math.log(2.0)))));
        return size;
    }

    private int calculateNumHashFunctions(int size, int expectedNumItems) {
        int numHashes = (int) Math.ceil((size / expectedNumItems) * Math.log(2.0));
        return numHashes;
    }
}

代码讲解

解释一下以上代码的各个部分:

  • BloomFilter 类的构造函数:在构造函数中,我们传入预期的元素数量 expectedNumItems 和期望的误判率 falsePositiveRate。然后,我们使用 calculateBitSetSize 和 calculateNumHashFunctions 方法计算位集合的大小和哈希函数的数量。接着,我们创建一个位集合 bitSet,并初始化一个 Random 对象。

  • add 方法:用于向布隆过滤器中添加元素。对于每个元素,我们使用不同的种子值(从0到numHashFunctions-1)来计算哈希值,并将对应的位设置为1。

  • contains 方法:用于检查元素是否存在于布隆过滤器中。对于每个元素,我们使用与添加操作相同的种子值来计算哈希值,并检查对应的位是否为1。如果任何一个位为0,则可以确定元素不存在于布隆过滤器中;否则,我们认为元素可能存在于布隆过滤器中。

  • hash 方法:用于计算哈希值。我们使用种子值作为随机数种子,并使用 Random 对象生成一个哈希值。然后,我们对哈希值取绝对值,并对位集合大小取模,以确保哈希值在位集合范围内。

  • calculateBitSetSize 方法:根据预期元素数量和期望的误判率,使用公式 (expectedNumItems * log(falsePositiveRate)) / log(1.0 / (Math.pow(2.0, Math.log(2.0)))) 计算位集合的大小,并返回结果。

  • calculateNumHashFunctions 方法:根据位集合的大小和预期元素数量,使用公式 (size / expectedNumItems) * log(2.0) 计算哈希函数的数量,并返回结果。

这是一个简单的布隆过滤器的 Java 实现。你可以根据自己的需求进行调整和扩展。注意,这个实现中没有考虑动态调整布隆过滤器大小或删除元素的功能。

测试代码

public class Main {
    public static void main(String[] args) {
        int expectedNumItems = 1000;
        double falsePositiveRate = 0.01;

        BloomFilter bloomFilter = new BloomFilter(expectedNumItems, falsePositiveRate);

        // 添加元素
        bloomFilter.add("apple");
        bloomFilter.add("banana");
        bloomFilter.add("orange");

        // 检查元素是否存在
        System.out.println(bloomFilter.contains("apple"));   // 输出: true
        System.out.println(bloomFilter.contains("banana"));  // 输出: true
        System.out.println(bloomFilter.contains("orange"));  // 输出: true
        System.out.println(bloomFilter.contains("grape"));   // 输出: false
        System.out.println(bloomFilter.contains("melon"));   // 输出: false
    }
}

运行结果

true
true
true
false
false

三、图书推荐

图书名称:

  • 《漫画算法:小灰的算法之旅》

图书介绍

【算法系列 | 7】深入解析查找算法之—布隆过滤器,算法系列,赠书活动,算法,数据结构,布隆过滤,原力计划

本书是《漫画算法:小灰的算法之旅》的续作,通过主人公小灰的心路历程,用漫画的形式讲述了多个数据结构、算法及复杂多变的算法面试题目。

第1章介绍了几种典型的排序算法,包括选择排序、插入排序、希尔排序、归并排序、基数排序。

第2章介绍了“树”结构的高级应用,包括二叉查找树、AVL树、红黑树、B树和B+树。

第3章介绍了“图”结构的概念,以及深度优先遍历、广度优先遍历、单源最短路径、多源最短路径算法。

第4章介绍了“查找”相关的算法和数据结构,包括二分查找算法、RK算法、KMP算法,以及“跳表”这种用于高效查找的数据结构。

第5章介绍了多种职场上流行的算法面试题目及详细的解题思路,例如螺旋遍历二维数组、寻找数组中第k大元素、求股票交易的更大收益等。

等不及的小伙伴,可以点击下方链接,先睹为快:漫画算法2

 参与方式 

图书数量:本次送出 4 本   !!!⭐️⭐️⭐️
活动时间:截止到 2023-08-11 12:00:00

抽奖方式:

  • 评论区随机抽取小伙伴!

留言内容,以下方式都可以:

  • 根据文章内容进行高质量评论

参与方式:关注博主、点赞、收藏,评论区留言 

中奖名单 

🍓🍓 获奖名单🍓🍓

 中奖名单:请关注博主动态

名单公布时间:2023-08-11 下午文章来源地址https://www.toymoban.com/news/detail-631507.html

到了这里,关于【算法系列 | 7】深入解析查找算法之—布隆过滤器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】布隆(Bloom Filter)过滤器

    Redisson系列文章: 【Redisson】Redisson–基础入门 【Redisson】Redisson–布隆(Bloom Filter)过滤器 【Redisson】Redisson–分布式锁的使用(推荐使用) 【分布式锁】Redisson分布式锁底层原理 【Redisson】Redisson–限流器 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的

    2024年02月05日
    浏览(34)
  • 深入理解PHP+Redis实现布隆过滤器(亿级大数据处理和黑客攻防必备)

    英文名称Bloom Filter,用于判断一个元素是否在一个大数据集合中,如果检测到存在则有可能存在,如果不存在则一定不存在。 Redis官网对于布隆过滤器的说明:https://redis.io/docs/data-types/probabilistic/bloom-filter/ 防止缓存穿透:用于快速判断某个商品数据是否存在于缓存中,如果存

    2024年04月09日
    浏览(45)
  • 【C++杂货铺】再谈哈希算法:位图 | 布隆过滤器 | 哈希切分

    给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 解决方案 : 遍历 :遍历这40亿个整数,去判断该数是否在这40亿个整数中。时间复杂度是 O ( N ) O(N) O ( N ) 。 set :用 set 将这40亿个整数存起来,然后去判断这个数是否在

    2024年02月08日
    浏览(57)
  • 布隆过滤器的原理

    布隆过滤器是一种用于检索一个元素是否在一个集合中的数据结构,具有高效的查询性能和较小的内存占用。 布隆过滤器的底层实现主要涉及以下几个步骤: 初始化数组: 首先,初始化一个比较大的数组,数组中的元素用二进制表示,初始值都为0。 Hash计算: 当一个新的元

    2024年01月18日
    浏览(40)
  • 布隆过滤器及其应用

    布隆过滤器是一种数据结构,具有快速插入和查找的特性,能确定某个字符串一定存在或者可能存在。布隆过滤器有着高效的空间利用率,它不存储具体数据,只存储数据的关键标识,所以占用的空间较小。它的查询结果可能会存在一定误差,但是误差总体可控,同时不支持

    2024年02月03日
    浏览(44)
  • 位图以及布隆过滤器

    本文主要讲解哈希思想的实际应用,位图和布隆过滤器。 讲解位图之前我们先来解答这样一道腾讯的面试题 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】 很多人立马就想到了用哈希表或者红黑树,因为足够

    2024年02月08日
    浏览(53)
  • 布隆过滤器详解

    本文全部代码地址 布隆过滤器是一种高效的数据结构,用于判断一个元素是否存在于一个集合中.它的主要优点是速度快,空间占用少,因此在需要快速判断某个元素是否在集合中的场合得到广泛引用. 布隆过滤器就是 一个大型的位数组和几个不一样的无偏hash函数. 所谓无偏就是

    2023年04月22日
    浏览(54)
  • Redis----布隆过滤器

    目录 背景 解决方案 什么是布隆过滤器 布隆过滤器的原理 一些其他运用 比如我们在观看新闻或者刷微博的时候,会不停地给我们推荐新的内容,我们发现几乎没有重复的,说明后台已经进行了去重处理,基于如何去重,Redis给出了高效的方案---布隆过滤器 1.记录已经浏览过

    2024年02月09日
    浏览(38)
  • 哈希的应用——布隆过滤器

    上一篇文章,我们学习了位图,位图在某些场景下是非常适用的,非常快捷方便。 但是,在文章的最后,我们也提出了位图的一些缺陷——比如位图只能映射整型数据,其它类型的数据则不行。 因为位图里面的元素去映射的其实就是下标嘛,而下标的话都是整型啊。 那有没

    2024年02月09日
    浏览(42)
  • Java实现布隆过滤器

    背景: 为预防大量黑客故意发起非法的时间查询请求,造成缓存击穿,建议采用布隆过滤器的方法解决。布隆过滤器通过一个很长的二进制向量和一系列随机映射函数(哈希函数)来记录与识别某个数据是否在一个集合中。如果数据不在集合中,能被识别出来,不需要到数据

    2024年02月01日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包