0

What is the generative function f_k(n) that produces all integers smaller than 2^k, that are sorted by number of bits set, then by value?

Expected output for k = 4:

f_4( 0) = b0000 =  0
--------------------
f_4( 1) = b0001 =  1
f_4( 2) = b0010 =  2
f_4( 3) = b0100 =  4
f_4( 4) = b1000 =  8
--------------------
f_4( 5) = b0011 =  3
f_4( 6) = b0101 =  5
f_4( 7) = b0110 =  6
f_4( 8) = b1001 =  9
f_4( 9) = b1010 = 10
f_4(10) = b1100 = 12
--------------------
f_4(11) = b0111 =  7
f_4(12) = b1011 = 11
f_4(13) = b1101 = 13
f_4(14) = b1110 = 14
--------------------
f_4(15) = b1111 = 15

I have made the observation that the binomial coefficient C(k,x) predicts how many integers with x bits set exist:

C(4,0) = 1
C(4,1) = 4
C(4,2) = 6
C(4,3) = 4
C(4,4) = 1

I have also found a way to generate all integers with x bits set.

Based on this I have come up with an solution.

int f(int k, int n) {
    int x = 0;
    int c = binomial(k, x);
    while (n >= c) {
        n -= c;
        ++x;
        c = binomial(k, x);
    }

    int v = (1 << x) - 1;  // v is exactly x many one bits
    for (int i = 0; i != n; ++i) {
        // next integer with x bits set
        int t = (v | (v - 1)) + 1;
        v = t | ((((t & -t) / (v & -v)) >> 1) - 1);
    }

    return v;
}

However my solution strikes me as inefficient, especially considering the while loop. I have the gut feeling that there are far more optimized solution to this, involving bit magic.

Bonus points for finding functions the return the next/previous element in the list, given the preceding/following one.

Community
  • 1
  • 1
Lukas Schmelzeisen
  • 2,934
  • 4
  • 24
  • 30

1 Answers1

0

I have found an efficient solution to the function returning the next element:

int next(int n, int v) {
    if (v == 0)
        return 1;

    int x = bitCount(v);
    // next integer with x bits set
    int t = (v | (v - 1)) + 1;
    v = t | ((((t & -t) / (v & -v)) >> 1) - 1);

    int m = ((1 << x) - 1) << (n - x);
    if (v > m)
        v = (1 << (x + 1)) - 1;
    if (v > (1 << n) - 1)
        v = 0;
    return v;
}
Lukas Schmelzeisen
  • 2,934
  • 4
  • 24
  • 30