算法:如何生成按二进制表示形式按1排序的数字?

时间:2018-07-16 16:56:21

标签: algorithm bit-manipulation

我正在寻找一个运算符,以按照其二进制表示形式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的副本,但是这个问题是在询问如何生成下一个数字而不是按索引返回

1 个答案:

答案 0 :(得分:0)

最后,我从heren-m's comment找到了答案。

基本思想是将数字视为01的排列。如果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