How do I generate all possible combinations of n-bit strings? I need to generate all combinations of 20-bit strings in a fastest way possible. (my current implementation is done with bitwise AND and right shift operation, but I am looking for a faster technique).
I need to store the bit-strings in an array (or list) for the corresponding decimal numbers, like --
0 --> 0 0 0
1 --> 0 0 1
2 --> 0 1 0
... etc.
any idea?
for (unsigned long i = 0; i < (1<<20); ++i) {
// do something with it
}
An unsigned long
is a sequence of bits.
If what you want is a string of characters '0'
and '1'
, then you could convert i
to that format each time. You might be able to get a speed-up taking advantage of the fact that consecutive numbers normally share a long initial substring. So you could do something like this:
char bitstring[21];
for (unsigned int i = 0; i < (1<<10); ++i) {
write_bitstring10(i, bitstring);
for (unsigned int j = 0; j < (1<<10); ++j) {
write_bitstring10(j, bitstring + 10);
// do something with bitstring
}
}
I've only increased from 1 loop to 2 there, but I do a little over 50% as much converting from bits to chars as before. You could experiment with the following:
'0'
, change it to a '1'
, and change all the '1'
s after it to '0'
.To fiendishly optimize write_bitstring
, multiples of 4 are good because on most architectures you can blit 4 characters at a time in a word write:
To start:
assert(CHAR_BIT == 8);
uint32_t bitstring[21 / 4]; // not char array, we need to ensure alignment
((char*)bitstring)[20] = 0; // nul terminate
Function definition:
const uint32_t little_endian_lookup = {
('0' << 24) | ('0' << 16) | ('0' << 8) | ('0' << 0),
('1' << 24) | ('0' << 16) | ('0' << 8) | ('0' << 0),
('1' << 24) | ('1' << 16) | ('0' << 8) | ('0' << 0),
// etc.
};
// might need big-endian version too
#define lookup little_endian_lookup // example of configuration
void write_bitstring20(unsigned long value, uint32_t *dst) {
dst[0] = lookup[(value & 0xF0000) >> 16];
dst[1] = lookup[(value & 0x0F000) >> 12];
dst[2] = lookup[(value & 0x00F00) >> 8];
dst[3] = lookup[(value & 0x000F0) >> 4];
dst[4] = lookup[(value & 0x0000F)];
}
I haven't tested any of this: obviously you're responsible for writing a benchmark that you can use to experiment.