1. 程式人生 > >種群計數------代碼之美

種群計數------代碼之美

擴展 sig 單位 64位 diff 位數 stat 比特 com

種群計數用於統計單位計算機字長所包含的1位的數量。

x是無符號整數,因此右移最高位填0

種群計數

基本方法1:

	int x = 4;//大概208條指令
int pop = 0; for (int i = 0; i<32; i++) { if (x & 1)pop = pop + 1; x = x >> 1; }

基本方法2:

	int x = 6;//大概120~160條指令
int pop = 0; while(x){ pop = pop + (x & 1); x = x >> 1; }

基本方法3:

	int x = 6;
int pop = 0; while(x){//循環體需要4或者5條指令,但循環次數等於x中包含的一位數的數目 pop = pop + 1; x = x &(x-1);//x&(x-1)的作用是將x的最低1位清零 }

查表法:

	int x = 6;//大概17條指令
	static char table[256] = { 0,1,1,2,1,2,2,3,...8 };
	int pop = table[x & 0xFF] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xFF] + table[x >> 24];

分治法:

技術分享圖片

 	int x = 7;
	x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
	x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
	x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
	x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
	x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);

 

int pop(unsigned x) {//21條指令
	x = x-((x >> 1) & 0x55555555);
	x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
	x = (x + (x >> 4)) & 0x0f0f0f0f;
	x = x + (x >> 8);
	x = x + (x >> 16);
	return x & 0x0000003f;
}

另外一種方法:

int pop(unsigned x) {//最大適用於62位字長,無法只通過簡單地擴展常數適用於64位字長
             //15條指令,但有一條無符號模指令,速度較慢
	unsigned n;
	n = (x >> 1) & 033333333333;//對每個三位字段進行1位計數
	x = x - n;
	n = (n >> 1) & 033333333333;
	x = x - n;
	x = (x + (x >> 3)) & 030707070707;//6位字段的1位數目和
	return x % 63;                   //將所以6位字段和相加
}

  

兩個字種群計數的和與差

pop(x)-pop(y)=pop(x)-(32-pop(~y))

      =pop(x)+pop(~y)-32

int popDiff(unsigned x, unsigned y) {//使用32條指令,若使用上面的分治法進行兩次運算和一次減法則需要43條指令
	x = x - ((x >> 1) & 0x55555555);
	x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
	y = ~y;
	y = y - ((y >> 1) & 0x555555555);
	y= (y & 0x33333333) + ((y >> 2) & 0x33333333);
	x = x + y;
	x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
	x = x + (x >> 8);
	x = x + (x >> 16);
	return (x & 0x0000007f) - 32;
}

兩個字的種群計數比較

程序返回負值表示pop(x)<pop(y),

返回0則表示pop(x)=pop(y),

返回正值表示pop(x)>pop(y)。

int popCmpr(unsigned xp, unsigned yp) {//對每個字中的單個比特位輪流清零,直到某個字的比特位全為0,那麽剩下的非零種群計數值的字就是較大者。大概50條指令
	unsigned x, y;
	x = xp&~yp;//清除同是1的比特位
	y = yp&~xp;//是1的比特位
     while (1) { if (x == 0)return y | -int(y); if (y == 0)return 1; x = x&(x - 1);//每個字 y = y&(y - 1);//清除一個1位 } }

數組中的1位種群計數

//處理2個一組的元素
#define CSA(h,l,a,b,c)\//處理進位 {unsigned u=a^b;unsigned v=c; h=(a&b)|(u&v);l=u^v;} int pop(unsigned x) {//21條指令 x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; x = x + (x >> 8); x = x + (x >> 16); return x & 0x0000003f; } int popArray(unsigned A[], int n) {//平均每字10.5條指令 int tot, i; unsigned ones, twos; tot = 0;//初始化 ones = 0; for (i = 0; i <= n - 2; i = i + 2) { CSA(twos, ones, ones, A[i], A[i + 1]); tot = tot + pop(twos); } tot = 2 * tot + pop(ones); if (n & 1)//如果還剩最後一個字,則把它的種群計數也加上 tot = tot + pop(A[i]); return tot; }
//CSA操作展開為:
u = ones^A[i]; v = A[i + 1]; twos = (ones&A[i]) | (u&v); ones = u^v;
//處理8個一組的元素
#define CSA(h,l,a,b,c) {unsigned u=a^b;unsigned v=c; h=(a&b)|(u&v);l=u^v;} int pop(unsigned x) {//21條指令 x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; x = x + (x >> 8); x = x + (x >> 16); return x & 0x0000003f; } int popArray(unsigned A[], int n) {//平均每字6.3條指令 int tot, i; unsigned ones, twos, twosA, twosB, fours, foursA, foursB, eights; tot = 0;//初始化 fours = twos = ones = 0; for (i = 0; i <= n - 8; i = i + 8) { CSA(twosA, ones, ones, A[i], A[i + 1]); CSA(twosB, ones, ones, A[i + 2], A[i + 3]); CSA(foursA, twos, twos, twosA, twosB); CSA(twosA, ones, ones, A[i + 4], A[i + 5]); CSA(twosB, ones, ones, A[i + 6], A[i + 7]); CSA(foursB, twos, twos, twosA, twosB); CSA(eights, fours, fours, foursA, foursB); tot = tot + pop(eights); } tot = 8 * tot + 4 * pop(fours) + 2 * pop(twos) + pop(ones); for(;i<n;i++)//n不一定能模8整除,所以簡單的加上最後的0~7個元素。 tot = tot + pop(A[i]); return tot; }

  技術分享圖片

技術分享圖片

種群計數------代碼之美