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

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

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

【问题讨论】:

    标签: php bit-manipulation array-map


    【解决方案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 代码供您使用,但请注意它的效率非常低。我使用这个算法是因为:

      1. 它是明确的,使用文字而不是数学技巧来完成工作,并且
      2. 在您无法假设 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 能够回归并重新思考。
      • 我已经实现了这个解决方案,它运行良好。我不得不承认,从算法的角度来看,第二个答案似乎更有效。对于任何感兴趣的人来说,实际用途是在牙科图像中标记处理过的牙齿。
      猜你喜欢
      • 2021-05-27
      • 1970-01-01
      • 2015-12-06
      • 1970-01-01
      • 1970-01-01
      • 2021-06-24
      • 1970-01-01
      • 2011-07-16
      • 2020-07-26
      相关资源
      最近更新 更多