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']
这有点令人困惑,因为我们用右边的最低有效位编写二进制表示,而左边的第 0th 元素显示数组。
从这些子数组中,只有具有最小 size 元素的那些会保留在结果中。所以在 size = 2 的情况下,不保留前 3 个:保留的第一个是 ['a','b']。
出于这个原因,算法有所改进,并且 mask 不是从 0 开始的,因为它已经确定找不到足够大的子集 mask 更小或等于大小。这就是为什么for 循环以该值开始的原因。但这并不重要。它也可以从零开始:
for (mask = 0; mask < total; mask++)
然后关于if:它测试是否在 mask 的二进制表示中设置了某个位:因为单个位 (1) 向左移动(所以它变成了什么比如二进制表示的 10000),然后用 mask 进行 AND-ed(使用 &)。例如,如果 mask 在某个时刻是 101001 并且移位的位是 10000,那么这个 & 操作是这样的:
mask: 101001
bit: 10000
------------
AND: 000000
另一个例子:
mask: 101001
bit: 1000
------------
AND: 001000
因此,if 条件测试 mask 中位置 i 的位(从二进制表示的右侧开始)是否已设置。如果已设置,则值 mask & (1 << i)) 将等于 (1 << i)(例如 1000),如果未设置此表达式的计算结果为 0。因此,通过执行 !== 0 测试,您可以有效地测试该位是放。只有在这种情况下,输入数组中的相应元素才会添加到子集中。
示例运行
假设我们使用这些参数调用函数:
(['a','b','c'], 2);
那么total = 8,mask从2到7。换算成二进制,mask取以下二进制值:
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
现在,检查 result 的长度,但是对于 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的长度,这次足够大了:
result.length == 2 // see above
results = [['b','a']]
...等等。
注意,results(复数)是一个数组数组,所以最终它会增长到:
results = [['b','a'], ['c','a'], ['c','b'], ['c','b','a']]
子数组的元素顺序相反,因为 i 的循环向后迭代。如果 i 从 0 开始递增(这可能没有任何问题),则子数组将正常排序。
限制
因为像 << 和 & 这样的位运算要求它们的参数是 32 位整数,所以手头的代码只适用于最多包含 32 个元素的输入数组。