使用Enumerable.Zip或按位AND逻辑查找所有子集

时间:2012-12-10 00:53:00

标签: c# algorithm linq bit-manipulation subset

我想找到整数数组的所有子集,所以我首先想到的是使用大小为n位的数字的二进制表示,其中1包括0而不包括。

例如:

int[] arr = { 3, 4, 5 };

通过数字0到7给我:

0,0,0
0,0,1
0,1,0
0,1,1
1,0,0
...etc

这映射到:

empty
5
4
4,5
3
...etc

要使用Enumerable.Zip进行映射。代码是:

public static IEnumerable<byte> ToBinary(this int value, int size)
{
    return ToBinaryStream(value, size).Reverse();
}

private static IEnumerable<byte> ToBinaryStream(int value, int size)
{
    if (value < 0)
        yield break;
    do
    {
        yield return (byte)(value & 1);
        --size;
    }
    while ((value = value >> 1) > 0 || size > 0);
}

int?[] arr = { 1,2,3,4 };

List<int[]> subsets = new List<int[]>();

for (int i = 0; i < (int)Math.Pow(2, (arr.Length)); i++)
{
    var subset = i.ToBinary(arr.Length).Zip(arr, (value, array) => value == 1 ? array : null)
        .Where(a => a != null).Cast<int>().ToArray();
    subsets.Add(subset);
}

似乎工作得很好。问题是我如何使用按位AND逻辑来做同样的事情?

我想将1000映射到数组中的第一个元素,将1001映射到第一个和最后一个元素。

1 个答案:

答案 0 :(得分:1)

要检查数字x是否应包含索引i的元素num

  1. i
  2. 左移1
  3. 按位和num
  4. 的结果
  5. 如果结果大于0,请包含x
  6. 以下是代码:

    int[] arr = { 1,2,3,4 };
    
    List<int[]> subsets = Enumerable
                  .Range(0, (int)Math.Pow(2, (arr.Length)))
                  .Select(num => arr.Where((x, i) => ((1 << i) & num) > 0).ToArray())
                  .ToList();
    

    它将num中的最低位视为数组中的第一个元素,以避免必须反转。顺便提一下有趣的想法:)