了解数组

时间:2016-07-14 13:14:20

标签: javascript arrays

你可以在这里看到(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

这意味着什么?

1 个答案:

答案 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个元素的输入数组。