按位与(&)运算符的数学函数是什么(JS)?
What mathematical function does bitwise AND (&) operator do (JS)?
一点上下文,当我试图解决 javascript 问题以找到所有可能的子集时,我正在查看另一个 SO post。我不是在问 JS 挑战,而是为什么会有它,它有什么数学意义?
这是从
复制粘贴的代码
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 & Math.pow(2, j))
行完成了什么。 mozilla doc 表示它对每个位对执行 AND 比较。为什么相关?
当我说相关时,我的意思是,例如使用 LEFT SHIFT,将 << 1 乘以 2。如果 a << b
且 b 为 1
,则其等效数学函数乘以二。我不明白数学函数 &
在这种情况下的作用。
当 i 中的第 j 位为 1 时,表达式 i & 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
条件将有两次为真35=].
让我们看看一个递增 1 的按位计数器:
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))
如果是这样,我们将该数组索引处的元素包含到结果中。
&
是按位 AND
比较。它的作用是逐位比较两个数字的二进制表示。
在您的示例中,1、2 和 3 在二进制中表示为 01、10 和 11。因此,1 & 2
= 01 & 10
= 0
。同样,1 & 3
= 01 & 11
= 01
= 1
.
if (i & Math.pow(2, j))
行正在对 i & 2^j
进行比较。
post 是关于生成集合的所有子集。以[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
.
这里是从 1
到 8
的数字的二进制值:
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)
因此,再次回到关于子集的原始 post - 第一个循环产生 0
和 1
的所有可能组合,固定长度为 n
,这等于我们集合中的元素数。
第二个循环产生 2^k
个值,等于:
001
010
100
...
其中 k
是我们原始集合中元素的索引。
而这个 2^k
,乘以第一个循环产生的组合,使用 &
或 AND
操作,给我们一个简单的结果,如果索引 [=29 处的元素=] 应包含在最终子集中。
在二进制中,任何 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 时为真。
一点上下文,当我试图解决 javascript 问题以找到所有可能的子集时,我正在查看另一个 SO post。我不是在问 JS 挑战,而是为什么会有它,它有什么数学意义?
这是从
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 & Math.pow(2, j))
行完成了什么。 mozilla doc 表示它对每个位对执行 AND 比较。为什么相关?
当我说相关时,我的意思是,例如使用 LEFT SHIFT,将 << 1 乘以 2。如果 a << b
且 b 为 1
,则其等效数学函数乘以二。我不明白数学函数 &
在这种情况下的作用。
当 i 中的第 j 位为 1 时,表达式 i & 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
条件将有两次为真35=].
让我们看看一个递增 1 的按位计数器:
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))
如果是这样,我们将该数组索引处的元素包含到结果中。
&
是按位 AND
比较。它的作用是逐位比较两个数字的二进制表示。
在您的示例中,1、2 和 3 在二进制中表示为 01、10 和 11。因此,1 & 2
= 01 & 10
= 0
。同样,1 & 3
= 01 & 11
= 01
= 1
.
if (i & Math.pow(2, j))
行正在对 i & 2^j
进行比较。
post 是关于生成集合的所有子集。以[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
.
这里是从 1
到 8
的数字的二进制值:
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)
因此,再次回到关于子集的原始 post - 第一个循环产生 0
和 1
的所有可能组合,固定长度为 n
,这等于我们集合中的元素数。
第二个循环产生 2^k
个值,等于:
001
010
100
...
其中 k
是我们原始集合中元素的索引。
而这个 2^k
,乘以第一个循环产生的组合,使用 &
或 AND
操作,给我们一个简单的结果,如果索引 [=29 处的元素=] 应包含在最终子集中。
在二进制中,任何 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 时为真。