python 布隆过滤器

复制代码
import mmh3
import redis
import math

class PyBloomFilter:
    # 内置100个随机种子
    SEEDS = [
        543, 460, 171, 876, 796, 607, 650, 81, 837, 545, 591, 946, 846, 521, 913, 636, 878, 735, 414, 372,
        344, 324, 223, 180, 327, 891, 798, 933, 493, 293, 836, 10, 6, 544, 924, 849, 438, 41, 862, 648, 338,
        465, 562, 693, 979, 52, 763, 103, 387, 374, 349, 94, 384, 680, 574, 480, 307, 580, 71, 535, 300, 53,
        481, 519, 644, 219, 686, 236, 424, 326, 244, 212, 909, 202, 951, 56, 812, 901, 926, 250, 507, 739, 371,
        63, 584, 154, 7, 284, 617, 332, 472, 140, 605, 262, 355, 526, 647, 923, 199, 518
    ]

    def __init__(self, capacity=2000000000, error_rate=0.00000001, conn=None, key='BloomFilter'):
        """
        初始化布隆过滤器。

        :param capacity: 预先估计要去重的数量。
        :param error_rate: 错误率。
        :param conn: Redis 的连接客户端。
        :param key: 在 Redis 中键的名字前缀。
        """
        self.m = math.ceil(capacity * math.log2(math.e) * math.log2(1 / error_rate))  # 需要的总 bit 数
        self.k = 1  # 需要最少的 hash 次数
        self.mem = math.ceil(self.m / 8 / 1024 / 1024)  # 需要的多少 M 内存
        self.block_size_gb = 10  # 每个内存块的大小(GB)
        self.block_size_bits = self.block_size_gb * 1024 * 1024 * 8  # 每个内存块的大小(bits)
        self.blocknum = min(math.ceil(self.mem / self.block_size_gb), 256)  # 需要多少个 10GB 的内存块
        self.seeds = self.SEEDS[:self.k]  # 只需要一个种子用于单哈希
        self.key = key
        self.N = 2 ** 31 - 1
        self.redis = conn

        print("self.m", self.m)
        print("self.k", self.k)
        print("self.mem", self.mem)
        print("self.block_size_bits", self.block_size_bits)
        print("self.blocknum", self.blocknum)
        print("self.seeds", self.seeds)

    def add(self, value):
        """
        将元素添加到布隆过滤器中。

        :param value: 要添加的元素。
        """
        # 计算 Redis 键名
        name = f"{self.key}_{ord(value[0]) % self.blocknum}"
        # 获取哈希值列表
        hashs = self.get_hashes(value)
        # 设置每个哈希位置的位为 1
        for hash in hashs:
            block_index = hash // self.block_size_bits
            bit_offset = hash % self.block_size_bits
            block_name = f"{name}_{block_index}"
            self.redis.setbit(block_name, bit_offset, 1)

    def is_exist(self, value):
        """
        检查元素是否存在于布隆过滤器中。

        :param value: 要检查的元素。
        :return: 如果存在返回 True,否则返回 False。
        """
        # 计算 Redis 键名
        name = f"{self.key}_{ord(value[0]) % self.blocknum}"
        # 获取哈希值列表
        hashs = self.get_hashes(value)
        exist = True
        # 检查每个哈希位置的位是否为 1
        for hash in hashs:
            block_index = hash // self.block_size_bits
            bit_offset = hash % self.block_size_bits
            block_name = f"{name}_{block_index}"
            exist = exist and bool(self.redis.getbit(block_name, bit_offset))
        return exist

    def get_hashes(self, value):
        """
        获取元素的哈希值列表。

        :param value: 要哈希的元素。
        :return: 哈希值列表。
        """
        hashs = list()
        for seed in self.seeds:
            hash_value = mmh3.hash(value, seed)
            if hash_value >= 0:
                hashs.append(hash_value)
            else:
                hashs.append(self.N - hash_value)
        return hashs


# 创建 Redis 连接池
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=8)
# 创建 Redis 客户端
conn = redis.Redis(connection_pool=pool)
# 创建布隆过滤器实例
bf = PyBloomFilter(conn=conn)

# 测试添加元素和检查元素是否存在
bf.add("example")
print(bf.is_exist("example"))  # 存在    输出应为 True
print(bf.is_exist("test"))     # 不存在  输出应为 False
复制代码

去重方式有那些
1. 数据库 唯一索引

  比如MySQL,es等
2. redis set


3. 布隆过滤器

4.  时间 + redis set

5. 

posted @   淋哥  阅读(6)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示