生成长度为n且设置了k位的所有二进制字符串

时间:2009-12-05 04:23:27

标签: algorithm binary permutation combinations bits

找到包含k位的所有长度为n的二进制字符串的最佳算法是什么?例如,如果n = 4且k = 3,则有......

0111
1011
1101
1110

我需要一个很好的方法来生成这些给定任何n和任何k,所以我更喜欢用字符串来完成。

12 个答案:

答案 0 :(得分:35)

此方法将生成具有正好N'1'位的所有整数。

来自https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation

  

计算按字典顺序排列的下位排列

     

假设我们在整数中有一个N位设置为1的模式,我们想要   字典意义上的N 1位的下一个排列。对于   例如,如果N为3且位模式为00010011,则为下一个模式   将是000101010001011000011001000110100001110000100011,   等等。以下是计算下一个的快速方法   排列。

unsigned int v; // current permutation of bits
unsigned int w; // next permutation of bits

unsigned int t = v | (v - 1); // t gets v's least significant 0 bits set to 1
// Next set to 1 the most significant bit to change,
// set to 0 the least significant ones, and add the necessary 1 bits.
w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
     

x86 CPU的__builtin_ctz(v) GNU C编译器内在函数返回尾随零的数量。如果您使用Microsoft编译器   x86,内在为_BitScanForward。这两个都发出bsf   指令,但其他架构可能有等效的指令。   如果没有,那么考虑使用其中一种方法来计算   前面提到的连续零位。这是另一个版本   由于它的除法运算符,它往往会变慢,但事实并非如此   要求计算尾随零。

unsigned int t = (v | (v - 1)) + 1;
w = t | ((((t & -t) / (v & -v)) >> 1) - 1);
     

感谢阿根廷的Dario Sneidermanis,他于2009年11月28日提供了此服务。

答案 1 :(得分:17)

<强>的Python

import itertools

def kbits(n, k):
    result = []
    for bits in itertools.combinations(range(n), k):
        s = ['0'] * n
        for bit in bits:
            s[bit] = '1'
        result.append(''.join(s))
    return result

print kbits(4, 3)

Output: ['1110', '1101', '1011', '0111']

说明:

基本上我们需要选择1位的位置。有n个选择k种方式在n个总位中选择k个比特。 itertools是一个很好的模块,它为我们这样做。 itertools.combinations(range(n),k)将从[0,1,2 ... n-1]中选择k位,然后只需在给定这些位索引的情况下构建字符串。

由于您没有使用Python,请在此处查看itertools.combinations的伪代码:

http://docs.python.org/library/itertools.html#itertools.combinations

应该易于用任何语言实现。

答案 2 :(得分:9)

忘记实现(“用字符串完成”显然是实现问题!) - 想想算法,为了Pete的缘故...就像在你的第一个标签,男人!

你要找的是一组N中的K项的所有组合(指数,0到N-1,设置位)。这显然是递归表达最简单的,例如,伪代码:

combinations(K, setN):
  if k > length(setN): return "no combinations possible"
  if k == 0: return "empty combination"
  # combinations INcluding the first item:
  return (first-item-of setN) combined combinations(K-1, all-but-first-of setN)
   union combinations(K-1, all-but-first-of setN)

,即第一个项目是存在还是不存在:如果存在,你有K-1离开(从尾巴也称为全冷却),如果不存在,仍然K离开去。

像SML或Haskell这样的模式匹配函数语言可能最能表达这种伪代码(程序代码,就像我的大爱Python一样,实际上可能通过包含过于丰富的功能来掩盖问题,例如itertools.combinations ,它为你做了所有艰苦的工作,因此从你身上隐藏它!)。

为此您最熟悉的是什么 - Scheme,SML,Haskell,......?我很乐意为你翻译上面的伪代码。当然,我也可以用像Python这样的语言来做 - 但是由于重点是让你理解这项作业的机制,我不会使用过于丰富的功能,比如itertools.combinations,而是在更明显的原语(例如head,tail和concatenation)上递归(以及递归消除,如果需要)。但请让我们知道您最熟悉的伪代码语言! (你明白你说的问题与“获得K项目的所有组合或范围(N)”完全相同,对吧?)。

答案 3 :(得分:4)

此C#方法返回一个创建所有组合的枚举器。因为它在枚举它时创建组合它只使用堆栈空间,所以它不受可以创建的组合数量的内存空间的限制。

这是我提出的第一个版本。它受到堆栈空间的限制,长度约为2700:

static IEnumerable<string> BinStrings(int length, int bits) {
  if (length == 1) {
    yield return bits.ToString();
  } else {
    if (length > bits) {
      foreach (string s in BinStrings(length - 1, bits)) {
        yield return "0" + s;
      }
    }
    if (bits > 0) {
      foreach (string s in BinStrings(length - 1, bits - 1)) {
        yield return "1" + s;
      }
    }
  }
}

这是第二个版本,它使用二进制拆分而不是拆分第一个字符,因此它可以更有效地使用堆栈。它只受到它在每次迭代中创建的字符串的内存空间的限制,我测试了它的长度为10000000:

static IEnumerable<string> BinStrings(int length, int bits) {
  if (length == 1) {
    yield return bits.ToString();
  } else {
    int first = length / 2;
    int last = length - first;
    int low = Math.Max(0, bits - last);
    int high = Math.Min(bits, first);
    for (int i = low; i <= high; i++) {
      foreach (string f in BinStrings(first, i)) {
        foreach (string l in BinStrings(last, bits - i)) {
          yield return f + l;
        }
      }
    }
  }
}

答案 4 :(得分:4)

该问题的许多标准解决方案的一个问题是生成整个字符串集,然后迭代这些字符串,这可能耗尽堆栈。除了最小的套装之外,它很快变得笨重。此外,在许多情况下,仅需要部分采样,但标准(递归)解决方案通常将问题切割成严重偏向一个方向的部分(例如,考虑所有具有零起始位的解决方案,然后全部一个起始位的解决方案。)

在许多情况下,更希望能够将一个字符串(指定元素选择)传递给一个函数并让它以这样的方式返回下一个位字符串,以便进行最小的更改(这是已知的)作为格雷码)并具有所有元素的表示。

Donald Knuth在他的分册3A第7.2.1.3节:生成所有组合中涵盖了大量算法。

有一种解决迭代格雷码算法的方法,用于从http://answers.yahoo.com/question/index?qid=20081208224633AA0gdMl处的n中选择k个元素的所有方法 链接到页面底部注释中的最终PHP代码(单击以展开它)。

答案 5 :(得分:1)

一种应该有效的算法:

generate-strings(prefix, len, numBits) -> String:
    if (len == 0):
        print prefix
        return
    if (len == numBits):
        print prefix + (len x "1")
    generate-strings(prefix + "0", len-1, numBits)
    generate-strings(prefix + "1", len-1, numBits)
祝你好运!

答案 6 :(得分:1)

以更通用的方式,下面的函数将为您提供N选择K问题的所有可能的索引组合,然后您可以将其应用于字符串或其他任何内容:

def generate_index_combinations(n, k):

    possible_combinations = []

    def walk(current_index, indexes_so_far=None):
        indexes_so_far = indexes_so_far or []
        if len(indexes_so_far) == k:
            indexes_so_far = tuple(indexes_so_far)
            possible_combinations.append(indexes_so_far)
            return
        if current_index == n:
            return
        walk(current_index + 1, indexes_so_far + [current_index])
        walk(current_index + 1, indexes_so_far)

    if k == 0:
        return []
    walk(0)
    return possible_combinations

答案 7 :(得分:1)

Python(功能样式)

使用python&#39; itertools.combinations,您可以生成k n reduce的所有选项,并将这些选项映射到from itertools import combinations from functools import reduce # not necessary in python 2.x def k_bits_on(k,n): one_at = lambda v,i:v[:i]+[1]+v[i+1:] return [tuple(reduce(one_at,c,[0]*n)) for c in combinations(range(n),k)] 的二进制数组}

In [4]: k_bits_on(2,5)
Out[4]:
[(0, 0, 0, 1, 1),
 (0, 0, 1, 0, 1),
 (0, 0, 1, 1, 0),
 (0, 1, 0, 0, 1),
 (0, 1, 0, 1, 0),
 (0, 1, 1, 0, 0),
 (1, 0, 0, 0, 1),
 (1, 0, 0, 1, 0),
 (1, 0, 1, 0, 0),
 (1, 1, 0, 0, 0)]

使用示例:

public class DepartmentDescription
{
   [Key]
   [Column(Order = 1)]
   public Guid DepartmentID { get; set; }
   [Key]
   [Column(Order = 2)]
   public Int32 LocaleID { get; set; }
   public String Description { get; set; }

   // Navigation Properties
   [ForeignKey("DepartmentID"), Required]
   public virtual Department Department { get; set; }

   [ForeignKey("LocaleID"), Required]
   public virtual Locale Locale { get; set; }

}

答案 8 :(得分:0)

一个可能的1.5班轮:

$ python -c 'import itertools; \
             print set([ n for n in itertools.permutations("0111", 4)])'

set([('1', '1', '1', '0'), ('0', '1', '1', '1'), ..., ('1', '0', '1', '1')])

..其中k1"0111"的数量。

itertools模块解释了其方法的等价物;查看permutation method的等效内容。

答案 9 :(得分:0)

我会尝试递归。

有n个数字,其中k为1。另一种观察这种情况的方法是k + 1个时隙序列,其中分布有n-k 0个。也就是说,(运行0s后跟1)k次,然后是另一次0运行。这些运行中的任何一个都可以是零长度,但总长度需要为n-k。

将此表示为k + 1个整数的数组。转换为递归底部的字符串。

递归调用深度n-k,这是一种在递归调用之前递增数组的一个元素然后递减它的方法,k + 1次。

在n-k的深度处,输出字符串。

int[] run = new int[k+1];

void recur(int depth) {
    if(depth == 0){
        output();
        return;
    }

    for(int i = 0; i < k + 1; ++i){
        ++run[i];
        recur(depth - 1);
        --run[i];
    }

public static void main(string[] arrrgghhs) {
    recur(n - k);
}

自从我完成Java以来​​已经有一段时间了,所以这段代码中可能存在一些错误,但这个想法应该有效。

答案 10 :(得分:0)

字符串是否比一组int更快?字符串前面的所有解决方案可能会在每次迭代时生成字符串的副本。

因此,最有效的方法可能是您追加的int或char数组。 Java有高效的可扩展容器,对吧?使用它,如果它比字符串快。或者如果BigInteger有效,它肯定是紧凑的,因为每个位只需要一点,而不是整个字节或int。但是然后迭代你需要的位数&amp;屏蔽一下,然后将屏蔽位移到下一位位置。所以可能更慢,除非JIT编译器现在都很擅长。

我会在原帖上发表评论,但我的业力还不够高。遗憾。

答案 11 :(得分:0)

很好的this问题(您需要以设置位的数量递增的顺序遍历所有子掩码),该问题已被标记为此重复项。

我们可以简单地遍历所有子掩码,将它们添加到向量中,然后根据设置的位数对它进行排序。

vector<int> v;
for(ll i=mask;i>0;i=(i-1)&mask)
    v.push_back(i);
auto cmp = [](const auto &a, const auto &b){
    return __builtin_popcountll(a) < __builtin_popcountll(b);
}
v.sort(v.begin(), v.end(), cmp);

另一种方法是,如果在第i次迭代中设置位的数量等于i,则对所有子掩码进行N次迭代,并向向量添加一个数字。

两种方法的复杂度均为O(n * 2 ^ n)