你可以在这里看到(Find all possible subset combos in an array?),提出的解决方案是
var sets = (function(input, size){
var results = [], result, mask, i, total = Math.pow(2, input.length);
for(mask = size; mask < total; mask++){
result = [];
i = input.length - 1;
do{
if( (mask & (1 << i)) !== 0){
result.push(input[i]);
}
}while(i--);
if( result.length >= size){
results.push(result);
}
}
return results;
})(['a','b','c','d','e','f'], 2);
console.log(sets);
首先,我想知道确切的尺寸和面具正在做什么。即为什么我们需要为我们的功能提供第二个参数。我也对按位运算符进行了研究,但我仍然不确定我们正在做什么和为什么
mask & (1 << i)) !== 0
这意味着什么?
答案 0 :(得分:3)
size 参数是必需的,因为最初的问题是产生最小尺寸的子集(2)。该参数允许您指定最小尺寸。这很好,因为您可以使用相同的代码来获取最小尺寸为3的所有子集。
变量 mask 获取可以使用 input.length 位进行的所有整数值。因此,以二进制表示, mask 采用以下值:
000000
000001
000010
000011
000100
... etc...
111100
111101
111110
111111
每1位表示输入数组中的相应元素应该是子集的一部分。因此, mask 的上述值表示以下子集:
[]
['a']
['b']
['a','b']
['c']
... etc ...
['c','d','e','f']
['a','c','d','e','f']
['b','c','d','e','f']
['a','b','c','d','e','f']
这有点令人困惑,因为我们在右边用最低有效位写二进制表示,而在左边用0 th 元素显示数组。
从这些子阵列中,只有那些 size 元素的子阵列才会保留在结果中。因此,在 size = 2的情况下,前3个不会被保留:第一个保留的是['a','b']
。
由于这个原因,算法有所改进, mask 不会从0开始,因为它已经确定找不到足够大的子集 mask 更小或等于 size 。这就是for
循环以该值开头的原因。但它并不重要。它也可以从零开始工作:
for (mask = 0; mask < total; mask++)
然后关于if
:它测试是否在 mask 的二进制表示中设置了某个位:因为单个位(1)向左移位(所以它在二进制表示中变成类似10000的东西)然后用掩码 AND -ed(带&
)。例如,如果 mask 处于特定时刻101001且移位位为10000,则此&
操作的工作方式如下:
mask: 101001
bit: 10000
------------
AND: 000000
另一个例子:
mask: 101001
bit: 1000
------------
AND: 001000
因此,if
条件测试是否设置掩码中位置 i (从二进制表示右侧开始)的位。如果设置了,则值mask & (1 << i))
将等于(1 << i)
(例如1000),如果未设置,则此表达式的计算结果为0.因此,通过执行!== 0
测试有效地测试该位是否已设置。只有在这种情况下,输入数组中的相应元素才会被添加到子集中。
假设我们用这些参数调用函数:
(['a','b','c'], 2);
然后总计 = 8,掩码从2运行到7.以二进制术语表示, mask 采用以下二进制值:< / p>
010
011
100
101
110
111
请注意,上表中的每个位(“列”)对应于数组的一个元素,其值(0或1)决定该元素是否应该在子数组中。
mask 的其他值并不重要:较小的值(如二进制001或000)表示元素太少的子数组 - 我们至少需要两个。较高的值(如1000,1001,...)有太多位:最左边的位与输入数组中的任何元素都不对应,因为我们在数组中只有3个元素。这意味着我们在上表中有 mask 的所有有趣值。
代码中发生的下一件事是在 mask 的特定值中找到1位。我们使用变量 i 来表示该位的0位数(从二进制表示的右侧开始的位置)。这个 i 将从2开始并减少到0(所以2,1和0:每个都指向 mask 中的三个位之一):
i = input.length - 1;
do { ... } while (i--);
对于给定的位号 i , mask 中的位值用以下内容提取:
mask & (1 << i)
所以让我们用 mask = 2(即二进制010)来完成上述所有操作。这些是计算的值:
result = []
i = 2
(1 << i) == 4 // 100 in binary: a 1-bit shifted twice to the left
mask & (1 << i) == 0 // because mask does not have that bit set
i 的下一次迭代:
i = 1
(1 << i) == 2 // 010 in binary: a 1-bit shifted once to the left
mask & (1 << i) == 2 // because mask has that bit set
result = ['b'] // because 'b' is the value at index i.
i 的最后一次迭代:
i = 0
(1 << i) == 1 // 001 in binary: a 1-bit not shifted at all
mask & (1 << i) == 0 // because mask does not have that bit set
现在,检查结果的长度,但是对于 mask 的特定值,它太小了:
result.length == 1 // see above, so nothing is added to the *results* (with 's')
mask == 2已经完成了所有操作,所以现在重复上面的 mask == 3(二进制011):
result = []
i = 2
(1 << i) == 4 // 100 in binary: a 1-bit shifted twice to the left
mask & (1 << i) == 0 // because mask does not have that bit set
i 的下一次迭代:
i = 1
(1 << i) == 2 // 010 in binary: a 1-bit shifted once to the left
mask & (1 << i) == 2 // because mask has that bit set
result = ['b'] // because 'b' is the value at index i.
i 的最后一次迭代(此处的结果不同):
i = 0
(1 << i) == 1 // 001 in binary: a 1-bit not shifted at all
mask & (1 << i) == 1 // because mask has that bit set as well (different!)
result = ['b','a'] // because 'a' is the value at index i.
现在,再次检查结果的长度,这次它足够大了:
result.length == 2 // see above
results = [['b','a']]
...等
请注意,结果(复数)是一个数组数组,所以最终会增长到:
results = [['b','a'], ['c','a'], ['c','b'], ['c','b','a']]
子数组的元素顺序相反,因为 i 的循环向后迭代。如果 i 从0开始递增(这可能没有任何问题),则子阵列将被正常排序。
因为像<<
和&
这样的位操作要求它们的参数是32位整数,所以手头的代码只适用于最多32个元素的输入数组。