概述
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]
整個類包含兩個主要的方法: setValue
和hasValue
。
先描述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。
其他代碼沒什麼好說的,執行一下就完事兒了。