PHP實現布隆過濾器

h6play發表於2020-12-01

說明

  • 程式碼僅供參考實現方式
  • 實際過程中 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"));
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章