PHP實現簡單的bitmap

概述

bitmap這個東西一直都是知道概念,沒有實際coding過,網上照抄了一個代碼,特喵的有點小問題,上一個自己debug過的版本。

作用

大量數據進行排序,查找和去重上可以使用這個策略來降低內存的使用

代碼

class BitMap
{
    public static $map = [];

    public static function setValue($value) {
        $index = self::getIndex($value);
        if (isset(self::$map[$index])) {
            self::$map[$index] |= 1 << ($value & 31);
        } else {
            self::$map[$index] = 1 << ($value & 31);
        }
    }

    public static function haxValue($value) {
        $index = self::getIndex($value);
        return isset(self::$map[$index]) ? (self::$map[$index] & (1 << ($value  & 31))) == (1 << ($value  & 31)) : false;
    }

    public static function display()
    {
        $keys = array_keys(self::$map);
        foreach ($keys as $key) {
            print "map index: {$key}, bit:";
            $temp = self::$map[$key];
            $bit_str = '';
            for ($i = 0; $i <= 31; $i++) {
                $bit_str = (1 & $temp) . $bit_str;
                $temp >>= 1;
            }
            echo "{$bit_str}\n";
        }

    }

    private static function getIndex($value) {
        return $value >> 5;
    }
}


$list = [1, 3, 3, 7, 25, 88, 0];
foreach ($list as $value) {
    BitMap::setValue($value);
}

BitMap::display();

var_dump(BitMap::haxValue(87));
var_dump(BitMap::haxValue(88));
var_dump(BitMap::haxValue(89));

策略

1個4字節的整型變量有32比特,所以我們用一個int來表示32個數字是否存在(哪個數字存在那麼那一位上就是1);
實際上:

$map[0] 表示 [0, 31]
$map[1] 表示 [32, 63]
$map[2] 表示 [64, 95]

整個類包含兩個主要的方法: setValuehasValue
先描述getIndex()的作用,實際上是將輸入值對32取模,可以按位右移5位(2^5=32)的方式或者floor($value / 32)等各種方式,按位操作會快嘛,用這個舒服。

再說setValue()方法,核心代碼就1行:

self::$map[$index] |= 1 << ($value & 31);

首先$value & 31實際上就是將輸入值對32取模。31轉換成二進制實際上就是5個1,拿着輸入值的後5位,來跟這5個1按位與,得到一個偏移量。記錄偏移量實際就是記錄了輸入值存在。 然後這裏有一個按位或的操作,起到了將多個偏移量聚合,且去重的作用。

其次我們來看hasValue()方法,核心代碼也就1行,暫時沒想到更好的寫法:

(self::$map[$index] & (1 << ($value  & 31))) == (1 << ($value  & 31))

在我們完成setValue()的行爲後,map中值的二進制展示大概是這樣:

bit:00000010000000000000000010001011

上述值我取的是$map[0],如果3是存在,那麼從右往左數4位,那一位一定是1,否則是0。所以判斷的方式是我們構造出來一個二進制的1000,拿着這個數去和$map[0]按位與。這裏有且僅有2種可能性:

  • 3存在:那麼返回二進制的1000
  • 3不存在: 那麼返回0

所以這裏也可以寫成:

 (self::$map[$index] & (1 << ($value  & 31))) != 0 

因爲我們構造的操作數有且僅有1位是1。

其他代碼沒什麼好說的,執行一下就完事兒了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章