PHP中基于位掩码获取数组值

Posted

技术标签:

【中文标题】PHP中基于位掩码获取数组值【英文标题】:Get array values based on bitmask in PHP 【发布时间】:2014-12-17 22:00:28 【问题描述】:

我有一个用于位掩码的 32 位整数和一个包含 32 个值的数组。如何从数组中仅获取那些索引对应于位掩码中非零位位置的值?

例如,假设位掩码是 49152,即二进制的 1100000000000000。因此,我必须从数组中获取索引为 14 和 15 的元素的值。

【问题讨论】:

【参考方案1】:

您需要在掩码上循环 32 步并测试它是否为“1”,如果设置了此位,您可以将元素复制到生成的数组中。

伪代码:

m = 0x00000001
j = 0
for i in 0 to 31 loop
  if ((mask & m) = m) then   // bit is set in mask
    result(j++) := input(i)
  end if
  m := m << 1     // shift left by 1 or multiply by 2
end loop

【讨论】:

当你提供的只是 pseudocode 而没有解决发布的问题时,你怎么能批评 Jeff Lambert 的真正 php 代码? 正如 Jeff 所说,我提出的算法(他实现的)速度提高了 7 倍! 这甚至不是 PHP。 #只是说【参考方案2】:

我已经实现了一个满足您需求的功能,此外:

可以处理更小或更大的数据类型

保存值而不仅仅是索引


function extractMask($mask, &$idxs, &$values) 
    for($i = 0, $valueToCompare = 1; $valueToCompare <= $mask; $i++, $valueToCompare <<= 1) 
        if ($mask & $valueToCompare) 
            $idxs[] = $i;
            $values[] = $valueToCompare;
        
    


// usage:
extractMask(49152, $idxs, $values);

// results:
var_dump($idxs);
var_dump($values);

为了印象,这里是一个 JS sn-p:

function extractMask(mask) 
  var result = idxs: [], values: [];
  for(i = 0, valueToCompare = 1; valueToCompare <= mask; i++, valueToCompare <<= 1) 
    if (mask & valueToCompare) 
      result.idxs.push(i);
      result.values.push(valueToCompare);
    
  
  return result;


//====================== UI ==========================
var anchor = document.getElementsByTagName("a")[0];
var input = document.getElementsByTagName("input")[0];
anchor.addEventListener("click", function(e) 
  var result = extractMask(input.value);
  console.log(result);
);
input.addEventListener("keyup", function (e) 
  if (e.keyCode == 13) 
    var result = extractMask(input.value);
    console.log(result);
  
);
//====================================================
<input value="49152" />
<a href="#">calculate</a>

【讨论】:

【参考方案3】:

这里有一些 PHP 代码供您使用,但请注意它的效率非常低。我使用这个算法是因为:

    它是明确的,使用文字而不是数学技巧来完成工作,并且 在您无法假设 2s 补码的底层实现但仍想以这种方式“思考”的情况下工作。 (尝试在 PHP 中“存储”位掩码并认为它​​们可以在 javascript 中工作)

请阅读 cmets 并实施 @Paebbels 解决方案。性能上的差距几乎是 7 倍,所以真的只在不经常使用的情况下才使用它。

它利用base_convert 将基数为 10 的整数转换为基数 2,将字符串拆分为字符数组,反转数组,然后遍历它以查找 1。

$mask = 49152; // 0xC000

// Find which positions in the mask contain a '1'
$bitArray = array_reverse(str_split(base_convert($mask, 10, 2)));

foreach($bitArray as $k => $v) 
    if($v) 
        echo $k . " is a one\n";
    

输出:

14 是一个

15 是一个

作为一个函数:

function extractElements($mask, array $input) 

    $output = array();
    $bitArray = array_reverse(str_split(base_convert($mask, 10, 2)));

    foreach($bitArray as $k => $v) 
        if($v && isset($input[$k])) 
            $output[] = $input[$k];
        
    

    return $output;


$mask = 76; // 0x4C
$input = [
    'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'
];

print_r(extractElements($mask, $input));

输出:

数组 ( [0] => 三 1 => 四 [2] => 七 )

【讨论】:

抱歉,这个解决方案会消耗大量内存和 CPU 周期来完成简单的位移和位比较。 内存量?对于 32 位整数掩码来说还不错。除非您有实时要求(.. 在 PHP 中..)或者您反复调用它,否则此解决方案是合适的。否则,我相信人们会发现您的回答很有帮助。 我的解决方案:3 words á 4 bytes = 12 bytes m, j, i,它没有调用指令。 -- 您的解决方案将掩码从 int 转换为 string;使用基于双精度值的基本转换(字符串到双精度、基本转换、到字符串转换);之后使用字符串操作(str_split),最后使用字符串反向操作。这些只是最初的海岸......在每次迭代中,您都在访问哈希列表并调用 isset 函数进行简单的位比较。您的代码使用 35 个调用指令。内存:中间字符串的每个字符存储在 2 个字节 (UTF-16) => 64 个字节中。 已更新以使对这种方法的警告更加明确。希望@Nikola Obreshkov 能够回归并重新思考。 我已经实现了这个解决方案,它运行良好。我不得不承认,从算法的角度来看,第二个答案似乎更有效。对于任何感兴趣的人来说,实际用途是在牙科图像中标记处理过的牙齿。

以上是关于PHP中基于位掩码获取数组值的主要内容,如果未能解决你的问题,请参考以下文章

当位掩码(标志)枚举变得太大时该怎么办

带有十进制数的 OAuth 2 范围以使用按位掩码

如何记录位掩码

来自谷歌服务的实时多人游戏问题(为您的房间配置添加专属位掩码)

如何更改此数组以将物理体赋予每个单独的节点(位掩码)?

什么是位掩码?