找到包含k位的所有长度为n的二进制字符串的最佳算法是什么?例如,如果n = 4且k = 3,则有......
0111
1011
1101
1110
我需要一个很好的方法来生成这些给定任何n和任何k,所以我更喜欢用字符串来完成。
答案 0 :(得分:35)
此方法将生成具有正好N'1'位的所有整数。
来自https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
计算按字典顺序排列的下位排列
假设我们在整数中有一个N位设置为1的模式,我们想要 字典意义上的N 1位的下一个排列。对于 例如,如果N为3且位模式为
00010011
,则为下一个模式 将是00010101
,00010110
,00011001
,00011010
,00011100
,00100011
, 等等。以下是计算下一个的快速方法 排列。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
&#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')])
..其中k
是1
中"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)