【算法】布隆过滤器

一、引言

        在现实世界的计算机科学问题中,我们经常需要判断一个元素是否属于一个集合。传统的做法是使用哈希表或者直接遍历集合,但这些方法在数据量较大时效率低下。布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于测试一个元素是否属于集合。本文将详细介绍布隆过滤器的原理、数据结构、使用场景、算法实现,并与其他算法进行对比,最后给出多语言实现及一个实际的服务应用场景代码框架。

二、算法原理

        布隆过滤器由一个很长的二进制向量和一系列随机映射函数组成。当我们要添加一个元素时,该元素会被多个哈希函数映射到二进制向量的不同位置,并将这些位置设为1。查询时,通过同样的哈希函数计算位置,如果所有对应的位置都是1,则该元素可能存在于集合中;如果有任何一个位置是0,则该元素一定不存在于集合中。

        布隆过滤器由一个比特数组(Bit Array)和多个哈希函数组成。

        初始时,所有的比特位都被置为 0。

        当元素被加入时,通过多个哈希函数计算出多个哈希值,然后将对应的比特位设置为 1。

        当查询一个元素是否存在时,同样通过多个哈希函数计算出哈希值,检查对应的比特位,如果所有对应的比特位都为 1,则该元素可能存在;如果有任何一个比特位为 0,则该元素一定不存在。

三、数据结构

布隆过滤器主要包含以下部分:

        一个大的位数组(bit array)。

        一组哈希函数。

四、使用场景

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

        网络爬虫过滤已抓取的URL。

        防止缓存穿透,如数据库查询缓存。

        查询重复元素,如邮件系统过滤重复邮件。

        空间敏感:当元素集合非常大,但只关心是否存在时。

        允许误报:当系统可以容忍少量错误判断时。

        快速查找:需要快速判断元素是否在集合中时。

五、算法实现

以下是布隆过滤器的简单实现步骤:

        初始化一个长度为m的位数组,并设置所有位为0。

        选择k个不同的哈希函数,它们将元素映射到位数组的位置。

        添加元素:对元素进行k次哈希,将得到的k个位置设为1。

        查询元素:对元素进行k次哈希,检查所有对应位置是否为1。

六、其他同类算法对比

        哈希表:精确匹配,空间占用大。

        位图(Bitmap):只能处理整数集合,不支持哈希函数。

        Cuckoo Filter:支持删除操作,空间利用率更高。

        后缀数组:适用于字符串搜索,空间和时间效率较高,但实现复杂。

        Trie树:适用于字符串集合,空间效率较高,但存在空间浪费。

七、多语言实现

布隆过滤器的伪代码实现:

java

// Java
public class BloomFilter {
    private BitSet bitSet;
    private int bitSetSize;
    private int addedElements;
    private static final int[] SEEDS = new int[]{5, 7, 11, 13, 31};

    public BloomFilter(int bitSetSize) {
        this.bitSetSize = bitSetSize;
        this.bitSet = new BitSet(bitSetSize);
        this.addedElements = 0;
    }

    public void add(String element) {
        for (int seed : SEEDS) {
            int hash = hash(element, seed);
            bitSet.set(hash);
        }
        addedElements++;
    }

    public boolean contains(String element) {
        for (int seed : SEEDS) {
            int hash = hash(element, seed);
            if (!bitSet.get(hash)) {
                return false;
            }
        }
        return true;
    }

    private int hash(String element, int seed) {
        // Implement a simple hash function
    }
}

python

# Python
class BloomFilter:
    def __init__(self, bit_array_size, hash_functions):
        self.bit_array = [0] * bit_array_size
        self.hash_functions = hash_functions

    def add(self, element):
        for hash_function in self.hash_functions:
            index = hash_function(element)
            self.bit_array[index] = 1

    def contains(self, element):
        return all(self.bit_array[hash_function(element)] for hash_function in self.hash_functions)

# Example hash functions
def hash_function_1(element):
    # Implement a simple hash function

def hash_function_2(element):
    # Implement another simple hash function

c++

// C++
#include <vector>
#include <functional>

class BloomFilter {
private:
    std::vector<bool> bit_array;
    std::vector<std::function<size_t(const std::string&)>> hash_functions;

public:
    BloomFilter(size_t size, const std::vector<std::function<size_t(const std::string&)>>& funcs)
        : bit_array(size, false), hash_functions(funcs) {}

    void add(const std::string& element) {
        for (const auto& func : hash_functions) {
            size_t index = func(element) % bit_array.size();
            bit_array[index] = true;
        }
    }

    bool contains(const std::string& element) const {
        for (const auto& func : hash_functions) {
            size_t index = func(element) % bit_array.size();
            if (!bit_array[index]) {
                return false;
            }
        }
        return true;
    }
};

Go

package main

import (
    "fmt"
    "github.com/willf/bloom"
)

func main() {
    filter := bloom.New(1000, 5) // 1000 items, 5 hash functions

    // Add items to the filter
    filter.Add([]byte("hello"))
    filter.Add([]byte("world"))

    // Test if items are in the filter
    if filter.Test([]byte("hello")) {
        fmt.Println("hello is in the filter")
    }
    if !filter.Test([]byte("missing")) {
        fmt.Println("missing is not in the filter")
    }
}

八、实际服务应用场景代码框架

使用布隆过滤器来防止缓存穿透的简单服务应用场景代码框架:

java

// Java - Cache Service with Bloom Filter
public class CacheService {
    private final BloomFilter<String> bloomFilter;
    private final Map<String, String> cache;

    public CacheService(int cacheSize, int bloomFilterSize) {
        this.cache = new HashMap<>(cacheSize);
        this.bloomFilter = new BloomFilter<>(bloomFilterSize);
    }

    public String get(String key) {
        if (!bloomFilter.contains(key)) {
            // The key is definitely not in the cache
            return null;
        }

        // The key might be in the cache, check the actual cache
        return cache.get(key);
    }

    public void put(String key, String value) {
        bloomFilter.add(key);
        cache.put(key, value);
    }
}

python

# Python - Cache Service with Bloom Filter
class CacheService:
    def __init__(self, cache_size, bloom_filter_size):
        self.cache = {}
        self.bloom_filter = BloomFilter(bloom_filter_size, [hash_function_1, hash_function_2])

    def get(self, key):
        if not self.bloom_filter.contains(key):
            # The key is definitely not in the cache
            return None
        # The key might be in the cache, check the actual cache
        return self.cache.get(key)

    def put(self, key, value):
        self.bloom_filter.add(key)
        self.cache[key] = value

# Define hash functions
def hash_function_1(element):
    # Implement a simple hash function

def hash_function_2(element):
    # Implement another simple hash function

c++

// C++ - Cache Service with Bloom Filter
#include <unordered_map>
#include <string>

class CacheService {
private:
    BloomFilter bloomFilter;
    std::unordered_map<std::string, std::string> cache;

public:
    CacheService(size_t bloomFilterSize, const std::vector<std::function<size_t(const std::string&)>>& hashFuncs)
        : bloomFilter(bloomFilterSize, hashFuncs) {}

    std::string get(const std::string& key) {
        if (!bloomFilter.contains(key)) {
            // The key is definitely not in the cache
            return "";
        }
        // The key might be in the cache, check the actual cache
        return cache[key];
    }

    void put(const std::string& key, const std::string& value) {
        bloomFilter.add(key);
        cache[key] = value;
    }
};

go

// Go - Cache Service with Bloom Filter
package main

import (
	"fmt"
	"github.com/willf/bloom"
)

type CacheService struct {
	bloomFilter *bloom.BloomFilter
	cache       map[string]string
}

func NewCacheService(bloomFilterSize int, cacheSize int) *CacheService {
	return &CacheService{
		bloomFilter: bloom.New(bloomFilterSize, 5),
		cache:       make(map[string]string, cacheSize),
	}
}

func (s *CacheService) Get(key string) (string, bool) {
	if !s.bloomFilter.Test([]byte(key)) {
		// The key is definitely not in the cache
		return "", false
	}
	// The key might be in the cache, check the actual cache
	value, exists := s.cache[key]
	return value, exists
}

func (s *CacheService) Put(key, value string) {
	s.bloomFilter.Add([]byte(key))
	s.cache[key] = value
}

func main() {
	service := NewCacheService(1000, 100)
	service.Put("hello", "world")

	if value, exists := service.Get("hello"); exists {
		fmt.Println("Found in cache:", value)
	}
}

布隆过滤器在 HBase 中的使用:

在 HBase 中,布隆过滤器主要用于以下两个场景:

行键查找(Get 操作):当客户端发起一个 Get 操作来查询特定的行键时,布隆过滤器可以快速判断该行键是否存在于某个 HFile 中,从而避免不必要的磁盘 I/O。

范围扫描(Scan 操作):当客户端执行一个 Scan 操作来检索一定范围内的行键时,布隆过滤器可以帮助跳过那些肯定不包含目标行键的 HFile,减少扫描的数据量。

HBase 支持以下几种布隆过滤器:

  • NONE:不使用布隆过滤器。
  • ROW:对行键使用布隆过滤器。
  • ROWCOL:对行键加列族:列限定符的组合使用布隆过滤器。
  • PREFIX:对行键的前缀使用布隆过滤器。

在 HBase 中配置布隆过滤器、布隆过滤器的配置可以在表级别进行,具体操作如下:

创建表时配置

// Java 代码示例
Configuration config = HBaseConfiguration.create();
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
HColumnDescriptor columnDescriptor = new HColumnDescriptor(familyName);

// 设置布隆过滤器类型为 ROW
columnDescriptor.setBloomFilterType(BloomType.ROW);

// 设置布隆过滤器的误报率,例如 0.01 表示 1% 的误报率
columnDescriptor.setBloomFilterFalsePositiveChance(0.01f);
tableDescriptor.addFamily(columnDescriptor);
admin.createTable(tableDescriptor);

修改现有表的配置

// Java 代码示例
Configuration config = HBaseConfiguration.create();
Admin admin = ConnectionFactory.createConnection(config).getAdmin();
TableName tableName = TableName.valueOf(tableName);
HColumnDescriptor columnDescriptor = new HColumnDescriptor(familyName);

// 获取表的描述符
HTableDescriptor tableDescriptor = admin.getTableDescriptor(tableName);

// 设置布隆过滤器类型为 ROW
columnDescriptor.setBloomFilterType(BloomType.ROW);

// 更新列族描述符
tableDescriptor.modifyFamily(columnDescriptor);
admin.modifyTable(tableName, tableDescriptor);
<think>嗯,用户想了解在Axure 9中如何实现导航栏的左右滑动动画。我之前处理过上下滑动的案例,但左右滑动可能有些不同。首先,我需要回忆一下Axure中的动态面板和交互事件设置。根据之前的引用资料,上下滑动主要通过动态面板的垂直拖动和边界限制来实现。那么左右滑动应该是类似的,但方向改为水平。 首先,用户可能需要一个导航栏,比如多个标签或菜单项,当点击左右箭头时,内容可以滑动切换。或者可能是通过拖动导航栏本身来左右滑动。需要明确用户的具体需求,但教程一般会涵盖常见方法。 根据引用[3]中的动态导航栏设置,使用动态面板和交互事件,比如单击时显示/隐藏二级菜单,并应用滑动动画。这可能适用于垂直方向,但左右滑动需要调整移动方向为水平。比如,设置动态面板的左右滑动事件,移动时改变X轴坐标。 另外,引用[1]和[2]提到了上下滑动的实现,通过设置动态面板的边界和移动范围。同样的原理可以应用到水平方向,限制左右滑动的范围,比如设置左侧和右侧的边界,确保导航栏在滑动时不超出可视区域。 可能需要以下步骤:准备动态面板作为导航栏容器,内部放置多个导航项水平排列。然后,添加交互事件,比如拖动时水平移动,或者添加左右箭头按钮,点击时滑动面板。需要设置移动的X轴范围,比如从0到负的面板宽度差,防止导航栏滑动过头。 另外,引用[4]提到实现上下滑动效果的方法,可以类比到左右滑动。使用动态面板的拖动事件,设置水平移动,并配置边界条件。例如,动态面板的宽度大于容器宽度时,允许左右滑动,并设置左侧和右侧的边界限制。 用户可能需要详细的步骤,包括元件准备、动态面板设置、交互事件配置(拖动或按钮点击)、边界限制和动画效果。需要注意动态面板的命名和层级结构,确保目标元件正确。同时,可能需要调整动画的持续时间和缓动效果,以优化用户体验。 总结步骤大致为:创建动态面板作为导航栏容器,内部放置内容;添加拖动或按钮触发事件;设置水平移动的动画;配置边界条件防止越界;测试并调整参数。需要确保引用相关的方法,比如动态面板的使用和交互事件设置,可能引用资料中的类似操作。</think>### Axure 9 实现导航栏左右滑动的分步教程 #### 1. **元件准备与布局** - **步骤1**:拖入一个**动态面板**作为导航栏容器,命名为“导航面板”,宽度设置为可视区域宽度(例如400px)。 - **步骤2**:在“导航面板”内部添加一个**动态面板**,命名为“滑动内容”,宽度需大于容器(例如800px),用于放置多个导航项(如按钮或标签)并水平排列[^3]。 #### 2. **设置滑动交互事件** - **拖动实现滑动**: - **步骤1**:选中“导航面板”,在右侧**交互**面板中添加**拖动时**事件。 - **步骤2**:选择目标元件为“滑动内容”,设置移动方式为**水平拖动**,勾选**限制边界**。 - **步骤3**:计算边界范围: - **左边界**:`0 - (滑动内容宽度 - 导航面板宽度)`(例如`0 - (800 - 400) = -400`) - **右边界**:固定为`0` 公式表示为: $$ x \geq -400 \quad \text{且} \quad x \leq 0 $$ [^2][^4] #### 3. **按钮控制滑动(进阶)** - **添加左右箭头按钮**: - **步骤1**:在“导航面板”两侧放置左右箭头按钮。 - **步骤2**:为**右箭头**添加**单击事件**:移动“滑动内容”向左(例如每次移动200px),边界判断逻辑同上。 ```plaintext 移动目标:滑动内容 方向:X轴,值:[[This.x - 200]] 动画:线性,500ms ``` - **步骤3**:为**左箭头**添加类似事件,方向改为向右移动(`X轴 + 200`)[^3]。 #### 4. **边界限制与动画优化** - **动态边界判断**: 在移动事件中添加**条件判断**,例如右箭头点击时需满足: $$ \text{滑动内容.x} > -400 $$ 否则禁止继续左移,避免空白区域[^1]。 - **动画效果**:建议使用**缓动动画**(如“平滑滚动”)提升交互体验[^4]。 #### 5. **预览与调试** - 使用**预览模式**测试滑动效果,根据实际内容宽度调整边界值和移动步长。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shinelord明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值