algorithmbitstring

generate all n bit binary numbers in a fastest way possible


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?


Solution

  • 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:

    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.