制作k& amp;的解决方案x = k范围(0,n)

时间:2018-01-14 21:31:03

标签: algorithm binary bit-manipulation bitwise-operators bitwise-and

如何有效地生成0,1,2...n内的所有数字。
(大n)。
对于固定的x和不同的k (0 <= k < n)k & x = k。 很容易发现,1中值k的所有位在1中也是x
但我无法计算所有这些 我使用DP查找x中设置位的所有子集和,以得出所有可能的解决方案。

但是这种方法证明在请求不同x的多个此类情况下效率低下。

我是否必须考虑需要改变的每一点以获得所有可能性?还有其他有效的方法吗?另外,我当然不想查看所有n

3 个答案:

答案 0 :(得分:2)

有一种巧妙的方法可以做到这一点

for(int i = x ;; i = x & (i - 1)){
     print i;
     if(i == 0)
        break;
}

请注意条件i = x & (i - 1)确保i始终减少且仅包含x中的位

请参阅here

中的Java代码

如果是x > n,那么i应该以{{1​​}}

开头

答案 1 :(得分:1)

首先请注意, x 中的0位表示 k 中必须为0的位,而 x 中的1位在 k 中可以是0或1。因此,算法应迭代 k 中的所有可能的位组合,其中 x 具有1位且结果数( k )不大比 n

这些组合最好通过使用Grey code序列之类的东西来产生,因为可以在一个恒定的时间内从一个位模式步进到下一个位模式。

示例:

x = 0b011010 (26)
n = 0b010000 (16)

k 生成的值是(按格雷码顺序排列):

    0b000000 ( =  0)
    0b000010 ( =  2)
    0b001010 ( = 10)
    0b001000 ( =  8)
    0b011000 ( = 24) too large: exclude
    0b011010 ( = 26) too large: exclude
    0b010010 ( = 18) too large: exclude
    0b010000 ( = 16)

由于使用格雷码方案,只有一位从一个组合变为另一个组合。这意味着数字不是按顺序生成的,有些可能太大(&gt; n )。这种不利因素仍然值得,因为按顺序生成它们会在每一步中产生更多的位变化。

以下是在JavaScript中实现此想法的代码段:

function get_nums(n, x) {
    // Solution array. Add zero as it is always a solution (assuming non-negative n)
    let result = [0], 
        k = 0,
        arr = [];  // Helper to follow Grey code sequence
    for (let i = 1; i <= n && i <= x; i <<= 1) { // Shift bit to the left
        if (x & i) { // This bit is set to 1 in x
            arr.push(i);
            k += i; // Set this bit in k
            if (k <= n) result.push(k); // Add k to solution array
            // Produce other matches following Grey code sequence
            for (let j = arr.length-2; j >= 0; j--) {
                arr.push(-arr[j]);
                k -= arr[j]; // Toggle a bit in k
                if (k <= n) result.push(k);
            }
        }
    }
    return result;
}

console.log(get_nums(16, 26));

请注意,输出未排序(因为使用了格雷码序列)。如果您需要它们,请应用一些基数排序(或散列)。

在JavaScript中,实现这样的基数排序非常容易,因为值是唯一的。但在其他语言中,您可以实现更明确,简化的radix sort。这是它的JavaScript函数:

function radix_sort_uniques(arr) {
    let result = {};
    // Add a property to the object for each value in the array
    for (let i of arr) result[i] = true;
    // Get those properties and convert them back to numeric data type (via map)
    // JavaScript will produce them in ascending order:
    return Object.keys(result).map(Number);
}

console.log(radix_sort_uniques([0, 2, 10, 8, 16]));

复杂度:

外部循环在 n 中每位位置迭代一次,即 log(n)次,而内循环每次迭代次数大约加倍。因此,在最坏的情况下(当 x 为0且内部循环始终执行时),我们按 2 log(n) 次,给出 O(n)时间复杂度。

x 修复时,复杂性也应该用 x 表示。假设 x 具有 b 1位,那么时间复杂度为 O(b + 2 b

答案 2 :(得分:1)

想象一下,我们在二进制表示中有x,如下所示:

x = 00001010110

在这种情况下,所有k都应k & x = k形式

x = 00001010110
k = 0000?0?0??0

其中?01。因此,我们必须获取1中的所有索引x(上例中的[1, 2, 4, 6]),并生成16的所有组合(示例中为0)和1位于相应的索引处:

C#实施:

private static IEnumerable<int> MySolution(int x) {
  int[] indexes = Enumerable
    .Range(0, 32)
    .Where(i => (x >> i) % 2 != 0)
    .ToArray();

  for (int value = 0; value < 1 << indexes.Length; ++value) 
    yield return indexes
      .Select((v, i) => ((value >> i) % 2) * (1 << v))
      .Sum();
}

测试:

Console.WriteLine(String.Join(", ", MySolution(5)));

结果(请注意,解决方案已整理):

0, 1, 4, 5

如果您想限制生成的解决方案,您可以修改循环:

private static IEnumerable<int> MySolution(int x, int n = -1) {
  int[] indexes = Enumerable
    .Range(0, 32)
    .Where(i => (x >> i) % 2 != 0)
    .ToArray();

  for (int value = 0; value < 1 << indexes.Length; ++value) {
    int result = indexes
      .Select((v, i) => ((value >> i) % 2) * (1 << v))
      .Sum();

    if (n < 0 || result <= n)  
      yield return;
    else
      break; 
  }
}