我正在寻找一个运算符,以按照其二进制表示形式1
的数量顺序生成2 ^ n以下的所有数字。
例如:
int next(int cur, int max) { /* ??? */ }
for (int i=0; i< 1<<6; i = next(i, 1<<6)) printf("%d ", i);
应该给出类似的内容:
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
(与1
相同的数字顺序无关紧要)
是否可以编写纯next()
函数而无需记住任何状态?
这看起来像是Encoding system sorted by the number of 1的副本,但是这个问题是在询问如何生成下一个数字而不是按索引返回
答案 0 :(得分:0)
最后,我从here和n-m's comment找到了答案。
基本思想是将数字视为0
和1
的排列。如果cur
不是具有相同数量1
的最大数字,我们可以简单地使用next置换算法来获得下一个置换算法(通过位技巧的实现),否则我们可以返回{{ 1}}。
这是一个简单的实现。我想可以通过一些魔术完全消除条件跳转和迭代:
(1 << (number_of_1(cur) + 1)) - 1
The link given by @rici提供了另一种解决方案:
// 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));
}
根据我对gcc-8.1.1 // 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));
}
的微基准测试:
-O3 -flto