次の関数を使用して、同じハミング重みで数値を (カウント順に) 列挙できることに注意してください。
int next(int n) { // get the next one with same # of bits set
int lo = n & -n; // lowest one bit
int lz = (n + lo) & ~n; // lowest zero bit above lo
n |= lz; // add lz to the set
n &= ~(lz - 1); // reset bits below lz
n |= (lz / lo / 2) - 1; // put back right number of bits at end
return n;
}
int prev(int n) { // get the prev one with same # of bits set
int y = ~n;
y &= -y; // lowest zero bit
n &= ~(y-1); // reset all bits below y
int z = n & -n; // lowest set bit
n &= ~z; // clear z bit
n |= (z - z / (2*y)); // add requried number of bits below z
return n;
}
例として、x = 5678 での prev() の繰り返し適用:
0: 00000001011000101110 (5678)
1: 00000001011000101101 (5677)
2: 00000001011000101011 (5675)
3: 00000001011000100111 (5671)
4: 00000001011000011110 (5662)
5: 00000001011000011101 (5661)
6: 00000001011000011011 (5659)
.....
したがって、理論的には、これを繰り返し適用することで数値のインデックスを計算できます。ただし、これには非常に時間がかかる場合があります。より良いアプローチは、いくつかの組み合わせを「ジャンプ」することです。
2 つのルールがあります。
1. if the number starts with: ..XXX10..01..1 we can replace it by ..XXX0..01..1
adding corresponding number of combinations
2. if the number starts with: ..XXX1..10..0 again replace it by XXX0..01..1 with corresponding number of combinations
次のアルゴリズムは、同じハミング重みを持つ数値の中から数値のインデックスを計算します(二項の高速な実装については気にしませんでした)。
#define LOG2(x) (__builtin_ffs(x)-1)
int C(int n, int k) { // simple implementation of binomial
int c = n - k;
if(k < c)
std::swap(k,c);
if(c == 0)
return 1;
if(k == n-1)
return n;
int b = k+1;
for(int i = k+2; i <= n; i++)
b = b*i;
for(int i = 2; i <= c; i++)
b = b / i;
return b;
}
int position_jumping(unsigned x) {
int index = 0;
while(1) {
if(x & 1) { // rule 1: x is of the form: ..XXX10..01..1
unsigned y = ~x;
unsigned lo = y & -y; // lowest zero bit
unsigned xz = x & ~(lo-1); // reset all bits below lo
unsigned lz = xz & -xz; // lowest one bit after lo
if(lz == 0) // we are in the first position!
return index;
int nn = LOG2(lz), kk = LOG2(lo)+1;
index += C(nn, kk); // C(n-1,k) where n = log lz and k = log lo + 1
x &= ~lz; //! clear lz bit
x |= lo; //! add lo
} else { // rule 2: x is of the form: ..XXX1..10..0
int lo = x & -x; // lowest set bit
int lz = (x + lo) & ~x; // lowest zero bit above lo
x &= ~(lz-1); // clear all bits below lz
int sh = lz / lo;
if(lz == 0) // special case meaning that lo is in the last position
sh=((1<<31) / lo)*2;
x |= sh-1;
int nn = LOG2(lz), kk = LOG2(sh);
if(nn == 0)
nn = 32;
index += C(nn, kk);
}
std::cout << "x: " << std::bitset<20>(x).to_string() << "; pos: " << index << "\n";
}
}
たとえば、数値 x=5678 を指定すると、アルゴリズムはそのインデックスを 4 回の繰り返しで計算します。
x: 00000001011000100111; pos: 4
x: 00000001011000001111; pos: 9
x: 00000001010000011111; pos: 135
x: 00000001000000111111; pos: 345
x: 00000000000001111111; pos: 1137
1137 は、同じハミング重みを持つ数のグループ内の 5678 の位置であることに注意してください。したがって、ハミングの重みが小さいすべての数値を考慮して、このインデックスを適宜シフトする必要があります。