我忘了有点黑客生成给定数量的1的所有整数。有没有人记得它(也可能解释一下)?
答案 0 :(得分:18)
更新测试计划 Live On Coliru
#include <utility>
#include <iostream>
#include <bitset>
using I = uint8_t;
auto dump(I v) { return std::bitset<sizeof(I) * __CHAR_BIT__>(v); }
I bit_twiddle_permute(I v) {
I 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.
I w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
return w;
}
int main() {
I p = 0b001001;
std::cout << dump(p) << "\n";
for (I n = bit_twiddle_permute(p); n>p; p = n, n = bit_twiddle_permute(p)) {
std::cout << dump(n) << "\n";
}
}
打印
00001001
00001010
00001100
00010001
00010010
00010100
00011000
00100001
00100010
00100100
00101000
00110000
01000001
01000010
01000100
01001000
01010000
01100000
10000001
10000010
10000100
10001000
10010000
10100000
11000000
假设我们在整数中有一个N位设置为1的模式,我们希望在字典意义上下一个N 1位的排列。例如,如果N是3并且位模式是00010011,则下一个模式将是00010101,00010110,00011001,00011010,00011100,00100011等。以下是计算下一个排列的快速方法。
unsigned int v; // current permutation of bits
unsigned int w; // next permutation of bits
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.
w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
x86 CPU的__builtin_ctz(v)
GNU C编译器内在函数返回尾随零的数量。如果您使用的是x86的Microsoft编译器,则内在函数为_BitScanForward
。这些都发出bsf指令,但是其他架构可以使用等价物。如果不是,则考虑使用其中一种方法来计算前面提到的连续零位。
这是另一个版本,由于它的除法运算符,它往往会更慢,但它 不需要计算尾随零。
unsigned int t = (v | (v - 1)) + 1;
w = t | ((((t & -t) / (v & -v)) >> 1) - 1);
感谢阿根廷的Dario Sneidermanis,他于2009年11月28日提供了此服务。
答案 1 :(得分:11)
对于有点黑客攻击,我想参考这个页面:Bit Twiddling Hacks。
关于您的具体问题,请阅读标题为Compute the lexicographically next bit permutation的部分。
按字典顺序计算下一位排列
假设我们在整数中有一个N位设置为1的模式,我们希望在字典意义上下一个N 1位的排列。例如,如果N是3并且位模式是00010011,则下一个模式将是00010101,00010110,00011001,00011010,00011100,00100011等。以下是计算下一个排列的快速方法。
unsigned int v; // current permutation of bits
unsigned int w; // next permutation of bits
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.
w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
x86 CPU的__builtin_ctz(v)GNU C编译器内部函数返回尾随零的数量。如果您使用的是x86的Microsoft编译器,则内在函数是_BitScanForward。这些都发出bsf指令,但是其他架构可以使用等价物。如果不是,则考虑使用其中一种方法来计算前面提到的连续零位。 这是另一个版本,由于它的除法运算符而往往较慢,但它不需要计算尾随零。
unsigned int t = (v | (v - 1)) + 1;
w = t | ((((t & -t) / (v & -v)) >> 1) - 1);
感谢阿根廷的Dario Sneidermanis,他于2009年11月28日提供了此服务。
答案 2 :(得分:2)
添加到@ sehe的答案中(最初来自Dario Sneidermanis也在http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation。)
#include <utility>
#include <iostream>
#include <bitset>
using I = uint8_t;
auto dump(I v) { return std::bitset<sizeof(I) * __CHAR_BIT__>(v); }
I bit_twiddle_permute(I v) {
I 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.
I w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
return w;
}
int main() {
I p = 0b001001;
std::cout << dump(p) << "\n";
for (I n = bit_twiddle_permute(p); n>p; p = n, n = bit_twiddle_permute(p))
{
std::cout << dump(n) << "\n";
}
}
bit_twiddle_permute(I v)存在边界问题。每当v是最后一个排列时,t都是1(例如2 ^ 8 - 1),(~t & -~t) = 0
,并且w是第一个比特排列,比v少一个1,除非{{1在这种情况下v = 000000000
。特别是如果你将p设置为0; main中的循环将产生七个1的所有排列,并且for循环的以下略微修改将循环遍历所有排列,设置为0,7,6,...,1位 -
w = 01111111
如果这是意图,或许值得评论。如果不是,修复是微不足道的,例如
for (I n = bit_twiddle_permute(p); n>p; n = bit_twiddle_permute(n))
因此需要额外的小简化
if (t == (I)(-1)) { return v >> __builtin_ctz(v); }
Dario Sneidermanis的想法的以下改编可能会更容易理解
I bit_twiddle_permute2(I v) {
I t = (v | (v - 1)) + 1;
if (t == 0) { return v >> __builtin_ctz(v); }
I w = t | ((~t & v) >> (__builtin_ctz(v) + 1));
return w;
}
int main() {
I p = 0b1;
cout << dump(p) << "\n";
for (I n = bit_twiddle_permute2(p); n>p; n = bit_twiddle_permute2(n)) {
cout << dump(n) << "\n";
}
}
或者与我在本文开头提到的问题类似的解决方案
I bit_twiddle_permute3(I v) {
int n = __builtin_ctz(v);
I s = v >> n;
I t = s + 1;
I w = (t << n) | ((~t & s) >> 1);
return w;
}