[英]Hamming weight based indexing
假設我們有一個整數的bitsize n=4;
我所描述的問題是如何根據漢明權重及其知道bitsize
值將數字索引到數組位置。 例如,具有16個用於bitsize 4的元素的數組將/可能如下所示:
|0|1|2|4|8|3|5|6|9|10|12|7|11|13|14|15|
元素按其漢明重量(必要)分組,並根據大小排序(不必要)。 只要您可以采取例如3(0011)進行一些操作並返回索引5,5(0101) - > 6等,則不需要排序。
將出現n
位的所有組合,並且不會重復。 例如, 3
bitsize將具有數組:
|0|1|2|4|3|5|6|7|
我最好有一個沒有循環的解決方案。 或任何討論simillar解決方案的論文。 或者最后只是拋出任何關於如何做到這一點的想法。
請注意,您可以使用以下函數枚舉具有相同漢明重量的數字(按計數順序):
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)
.....
因此,從理論上講,您可以通過重復應用來計算數字的索引。 然而,這可能需要很長時間。 更好的方法是“跳過”某些組合。
有兩個規則:
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的位置。 因此,您必須相應地移動此索引以考慮具有較小漢明權重的所有數字
這是一個概念工作,只是為了開始討論。
第一步是最困難的 - 使用近似值來計算因子。
還有更好的想法嗎?
#include <stdio.h>
#include <math.h>
//gamma function using Lanczos approximation formula
//output result in log base e
//use exp() to convert back
//has a nice side effect: can store large values in small [power of e] form
double logGamma(double x)
{
double tmp = (x-0.5) * log(x+4.5) - (x+4.5);
double ser = 1.0 + 76.18009173 / (x+0) - 86.50532033 / (x+1)
+ 24.01409822 / (x+2) - 1.231739516 / (x+3)
+ 0.00120858003 / (x+4) - 0.00000536382 / (x+5);
return tmp + log(ser * sqrt(2*M_PI) );
}
//result from logGamma() are actually (n-1)!
double combination(int n, int r)
{
return exp(logGamma(n+1)-( logGamma(r+1) + logGamma(n-r+1) ));
}
//primitive hamming weight counter
int hWeight(int x)
{
int count, y;
for (count=0, y=x; y; count++)
y &= y-1;
return count;
}
//-------------------------------------------------------------------------------------
//recursively find the previous group's "hamming weight member count" and sum them
int rCummGroupCount(int bitsize, int hw)
{
if (hw <= 0 || hw == bitsize)
return 1;
else
return round(combination(bitsize, hw)) + rCummGroupCount(bitsize,hw-1);
}
//-------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int bitsize = 4, integer = 14;
int hw = hWeight(integer);
int groupStartIndex = rCummGroupCount(bitsize,hw-1);
printf("bitsize: %d\n", bitsize);
printf("integer: %d hamming weight: %d\n", integer, hw);
printf("group start index: %d\n", groupStartIndex);
}
輸出:
bitsize:4
整數:14漢明重量:3
小組起始指數:11
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.