Reputation: 2998
I am finding an operator to generate all numbers below 2^n in the order of the number of 1
in its binary representation.
For example:
int next(int cur, int max) { /* ??? */ }
for (int i=0; i< 1<<6; i = next(i, 1<<6)) printf("%d ", i);
should give out something like:
0 1 2 4 8 16 32 3 5 6 9 10 12 17 18 20 24 33 34 36 40 48 7 11 13 14 19 21 22 25 26 28 35 37 38 41 42 44 49 50 52 56 15 23 27 29 30 39 43 45 46 51 53 54 57 58 60 31 47 55 59 61 62 63
(the order of numbers with the same number of 1
doesn't matter)
Is it possible to write a pure next()
function without remembering any state?
This may looks like a duplicate of Encoding system sorted by the number of 1, but this question is asking about how to generate the next number instead of returning by index
Upvotes: 1
Views: 338
Reputation: 2998
Finally I found the answer from here and n-m's comment.
The basic idea is to consider the number as a permutation of 0
and 1
s. If cur
is not the largest number with the same number of 1
s, we can simply use next permutation algorithm to get the next one (with the implementation of bit tricks), otherwise we can return (1 << (number_of_1(cur) + 1)) - 1
.
Here is an simple implementation. I guess it is possible to completely eliminate conditional jumps and iterations with some bit magic:
// https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
static int next_perm(unsigned int v)
{
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.
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
}
static int next(unsigned int cur, unsigned int max) {
if (cur + 1 == max) return max;
if (((cur - 1) | cur) >= max - 1) return (1 << (__builtin_popcount(cur) + 1)) - 1;
return next_perm(cur);
}
int main() {
for (volatile int i=0; i< 1<< 30; i = next(i, 1<<30));
}
The link given by @rici provides another solution:
// end with zero
template<typename UnsignedInteger>
UnsignedInteger next_combination(UnsignedInteger comb, UnsignedInteger mask) {
UnsignedInteger last_one = comb & -comb;
UnsignedInteger last_zero = (comb + last_one) &~ comb & mask;
if (last_zero) return comb + last_one + (last_zero / (last_one * 2)) - 1;
else if (last_one > 1) return mask / (last_one / 2);
else return ~comb & 1;
}
int main() {
for (volatile int i = 1; i; i = next_combination(i, (1 << 30) -1));
}
According to my micro-benchmark with gcc-8.1.1 -O3 -flto
:
➜ bitmagic git:(master) ✗ time ./next-1
./next-1 4.27s user 0.01s system 99% cpu 4.313 total
➜ bitmagic git:(master) ✗ time ./next-2
./next-2 12.42s user 0.00s system 99% cpu 12.436 total
Upvotes: 1