• 代码仅供参考实现方式
  • 实际过程中 PHP 如果需要用到布隆过滤器还是得依赖于 Redis 布隆过滤器扩展
  • 毕竟 PHP 并非常驻内存的应用程序,存储在 BitSet表 里面的数据当进程结束后会丢失
 
<?php

/**
 * BitSet 模拟BitSet 在PHP中可以使用Array代替
 */
class BitSet {
    protected $bit = [];

    public function add($index) {
        $this->bit[$index] = 1;
    }

    public function has($index) {
        if(isset($this->bit[$index])) {
            return true;
        }
        return false;
    }
}

/**
 * Hash算法
 */
class HashFunction
{
    protected $size;
    protected $seed;

    public function __construct($size, $seed)
    {
        $this->size = $size;
        $this->seed = $seed;
    }

    public function hash($value)
    {
        $r = 0;
        $l = strlen($value);
        for ($i = 0; $i < $l; $i++) {
            $r = $this->seed * $r + ord($value[$i]);
        }
        return ($this->size - 1) & $r;
    }
}

/**
 * 布隆过滤器
 */
class BloomFilter
{

    /** @var int 一个长度为10 亿的比特位 */
    protected $size = 256 << 22;

    /** @var int[] 为了降低错误率,使用加法hash算法,所以定义一个8个元素的质数数组 */
    protected $seeds = [3, 5, 7, 11, 13, 31, 37, 61];

    /** @var HashFunction[] 相当于构建 8 个不同的hash算法 */
    protected $functions = [];

    /** @var BitSet[] 初始化布隆过滤器的 bitmap */
    protected $bitset = [];

    public function __construct($size = null, $seeds = null)
    {
        if($seeds != null) {
            $this->size = $size;
        }
        if($seeds != null) {
            $this->seeds = $seeds;
        }
        foreach ($this->seeds as $v) {
            $this->functions[$v] = new HashFunction($this->size, $v);
            $this->bitset[$v] = new BitSet();
        }
    }

    /**
     * @param string $str
     */
    public function add($str) {
        if($str != null) {
            foreach ($this->functions as $k => $fun) {
                $this->bitset[$k]->add($fun->hash($str));
            }
        }
    }

    /**
     * @param string $str
     * @return bool
     */
    public function has($str) {
        if($str != null) {
            foreach ($this->functions as $k => $fun) {
                if(!$this->bitset[$k]->has($fun->hash($str))) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
}


function d($str) {
    if(is_array($str)) {
        echo "[Array]:\n";
        print_r($str);
        echo "\n";
    } else if(is_object($str)) {
        echo "[Object]:\n";
        print_r($str);
        echo "\n";
    } else if(is_bool($str)) {
        echo "[Boolean]: " . ($str ? "true" : "false") . "\n";
    } else {
        echo "[String]: " . $str . "\n";
    }
}

/*
 * 测试函数
 */

d("初始化布隆过滤器");
$f = new BloomFilter;
$l1 = 10000;
$l2 = 10;

d("添加初始数据");
for ($i = 0; $i < $l1; $i ++) {
    $f->add("#" . $i);
//    d("add #{$i}");
}

d("测试判断随机数 {$l2}个");
for ($i = 0; $i < $l2; $i ++) {
//    $s = "#" . rand($l2, $l2 + 1000);
//    $s = "#0";
    $s = "#" . rand(0, $l1 * 2);
    $r = $f->has($s);
    d("判断数字 {$s} >> " . ($r ? "true" : "false"));
}