【问题标题】:What mathematical function does bitwise AND (&) operator do (JS)?按位与 (&) 运算符执行什么数学函数 (JS)?
【发布时间】:2017-12-13 21:04:43
【问题描述】:

一点上下文,当我试图解决 javascript 问题以查找所有可能的子集时,我正在查看另一篇 SO 帖子。我不是问 JS 挑战,而是问它为什么会出现,它有什么数学意义?

这里是this SO post的代码复制粘贴

var arr = [1, 2, 3];

function generatePowerSet(array) {
  var result = [];
  result.push([]);

  for (var i = 1; i < Math.pow(2, array.length); i++, result.push(subset))
    for (var j = 0, subset = []; j < array.length; j++)
      if (i & Math.pow(2, j))
        subset.push(array[j]);

  return result;
}

console.log(generatePowerSet(arr));

我不明白if (i &amp; Math.pow(2, j)) 行正在完成什么。 mozilla doc 表示它对每个位对执行 AND 比较。为什么相关?

当我说相关时,我的意思是,例如使用 LEFT SHIFT,执行 a << b 和b 是1,则它的等效数学函数乘以2。在这种情况下,我不明白 &amp; 的数学函数是什么。

【问题讨论】:

  • 这是一个数学AND,来自布尔代数。并非所有数学都是基于算术的。
  • 我认为输入和运算结果之间没有任何有助于理解的数学关系。只需查看位模式即可。

标签: javascript bitwise-operators


【解决方案1】:

i 中的第 jth 位为 1 时,表达式i &amp; Math.pow(2, j) 给出一个非零值(从最低有效位开始计数,即 0th 位)。

这可以通过示例得到最好的解释。假设 i 在某个时刻是 10;二进制:1010。现在让 j 为 0。然后:

     i & Math.pow(2, j)
 ==  10 & Math.pow(2, 0)
 ==  10 & 1
 ==  0b1010 & 0b0001
 ==  0b0000

第二个值 (0b0001) 的作用是过滤:它从第一个值中过滤掉一个位。看看当 j 为 1 时会发生什么:

     i & Math.pow(2, j)
 ==  10 & Math.pow(2, 1)
 ==  10 & 2
 ==  0b1010 & 0b0010
 ==  0b0010

if 条件对于 j 的这个值将因此为真。

由于 i 有两个 1 位,因此对于 i 的特定值,if 条件将有两次为真。

【讨论】:

    【解决方案2】:

    让我们看一个递增一的按位计数器:

     0 -> 000
     1 -> 001
     2 -> 010
     3 -> 011
     4 -> 100
     ...
     7 -> 111
    

    如您所见,如果我们想象 1 为真而 0 为假,它实际上会生成所有可能的 4 个布尔组合。所以它实际上非常适合生成一个powerSet。我们只需要一个计数器 (i) 来处理这些值,然后我们需要将布尔值转换为数组元素。为此,我们需要从左到右遍历它

     for (var j = 0, subset = []; j < array.length; j++)
    

    并检查是否检查了一个布尔值(位)

    if (i & Math.pow(2, j))
    

    如果是这样,我们将该数组索引处的元素包含到结果中。

    【讨论】:

      【解决方案3】:

      &amp; 是按位 AND 比较。它的作用是逐位比较两个数字的二进制表示。

      在您的示例中,1、2 和 3 以二进制形式表示为 01、10 和 11。因此,1 &amp; 2 = 01 &amp; 10 = 0。同样,1 &amp; 3 = 01 &amp; 11 = 01 = 1

      if (i &amp; Math.pow(2, j)) 行正在对 i &amp; 2^j 进行此比较。

      【讨论】:

        【解决方案4】:

        那篇文章是关于生成集合的所有子集的。以[1, 2, 3] 为例。

        现在让我们看看你拥有的代码:

        for (var i = 1; i < Math.pow(2, array.length); i++, result.push(subset))
        

        i 的值从1 循环到2^Length_Of_Set,在我们的示例中为2^3 = 8

        这里是从18 的数字的二进制值:

        1 = 001
        2 = 010
        3 = 011
        4 = 100
        5 = 101
        6 = 110
        7 = 111
        

        如果我们说,每个3 位代表我们原始集合中的值,如果它是1,那么让我们将它包含在子集中。这意味着上面的7 二进制值代表了我们原始集合的所有可能子集。

        两个数字之间的逻辑AND 运算会导致它们的二进制表示之间进行按位相乘:

        1 & 2^0 = 001 * 001 = 001 (0*0, 0*0, 1*1)
        2 & 2^0 = 010 * 001 = 000 (0*0, 1*0, 0*1)
        ...
        7 & 2^2 = 111 * 100 = 100 (1*1, 1*0, 1*0)
        

        所以,再次回到关于子集的原始帖子 - 第一个循环产生 01 的所有可能组合,固定长度为 n,等于我们集合中的元素数量。

        第二个循环产生2^k 值,等于:

        001
        010
        100
        ...
        

        其中k 是我们原始集合中元素的索引。

        这个2^k 乘以第一个循环产生的组合,使用&amp;AND 操作,给我们一个简单的结果,即索引k 处的元素是否应该包含在最终子集中。

        【讨论】:

          【解决方案5】:

          在二进制中,任何 2 的幂都只是一个 1 后跟零。简要说明:

          • 2^0: 1
          • 2^1: 10
          • 2^2: 100

          等等。

          另外,如你所知,A 的幂集是 A 的所有子集的集合。方便的是,如果 A 有 n 个元素,那么 A 的子集可以用 n 位表示,其中每个位对应一个元素A. 如果位为 1,我们包含该元素,如果不是,我们不包含。

          所以要得到一个幂集,如果我们可以遍历从 1 到 2^n 的每个数字,并相应地创建子集。所以我们只需要一种方法来根据给定的数字生成子集。

          所以行:

          // get each number from 1 -> 2^n. These represent each subset.
          for (var i = 1; i < Math.pow(2, array.length); i++, result.push(subset))
            // Now we need to see which elements of the original array
            // go into this subset. So we check each one individually:
            for (var j = 0, subset = []; j < array.length; j++)
              // And finally, we only put this item in the subset if
              // the bit at index we are at is 1.
              if (i & Math.pow(2, j))
                subset.push(array[j]);
          

          关键点是我们应该仅在同一索引的位为 1 时才包含该元素。所以要做到这一点,我们使用按位运算符。现在,我们知道在二进制中,除了第 j 个位置的单个 1 之外,2^j 都是零。因此,如果您将其与i 本身按位与,则如果i 的第 j 个位置的位为 0(假),则结果将全为 0,否则为 2^n(真)。

          所以 TL;DR:

          (i & Math.pow(2, j))
          

          当且仅当数字 i 的二进制表示的第 j' 位为 1 时为真。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-07-21
            • 1970-01-01
            • 1970-01-01
            • 2014-12-23
            • 1970-01-01
            • 2017-03-08
            • 2023-03-05
            • 2011-11-04
            相关资源
            最近更新 更多