种群计数 (pop_count)
//高效程序的奧秘hacker's delight Henry S. Warren, Jr. 著馮速譯
//第5 章位計數(shù)5.1 1 位計數(shù)
// 應用:
//???? Hamming 距離是矢量間取不同值的對應位的數(shù)目,即dist(x, y) = pop(x ^ y);
//?? 稀疏數(shù)組A 壓縮后的簡便訪問方式,可以利用位串數(shù)組BITS 記錄相應下標有定義的元素,
// 如每個有定義的A[i],BITS 的第I 個元素是1 位。
?
#include "iostream"
#include "bitset"
#include "limits"
#include "time.h"
?
using namespace std;
?
#define out2(T) cout<<bitset<numeric_limits<unsigned int>::digits>(T)<<endl;
#define SIZE(T) (numeric_limits<unsigned T>::digits)
?
bool SHR31(int x)?????????? //邏輯右移位
{
???? return (unsigned int)x>>31;
}
?
#define HOUT(x,y,z)??? hex_out(x,y,z)
?
inline void hex(int x)
{
???? int w = cout.width();
???? cout << "0x";
???? cout.width(8); cout.fill('0');
???? cout << hex << x << "? ";
???? cout.width(w);
}
inline void hex(unsigned __int64 x)
{
???? int w = cout.width();
???? cout << "0x";
???? cout.width(16); cout.fill('0');
???? printf("%I64x?? ",x);
???? cout.width(w);
}
void hex_out(int x, int y, int z)
{
???? hex(x); hex(y); hex(z); cout << endl;
}
?
// 循環(huán)左移位
int rotate(unsigned int x, int n)
{
???? return ? (x<<n) | (x >> (32-n));
}
?
// 統(tǒng)計x 中位1 的個數(shù)
// 首先設置每個2 位字段為原來的兩個單位的和,然后,求相鄰位字段的和,把結果放入相應的位字段,以此類推。
int pop(unsigned int x)
{
???? 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);
???? return x;
}
?
// 對x 的第一個賦值基于下面這個相當巧妙的公式的前兩項
// pop(x) = x - floor(x/2) - floor(x/4) - ... - floor(x/2^31);
// 設字是b3b2b1b0
// bi = floor(x/2^i) - 2*floor(x/2^(i+1))
int pop2(unsigned int x)
{
???? 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 & 0x00000003F;
}
?
// [HAK, item169]
// http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html
// 在DEC PDP-10計算機上只需要10 個指令
// 它是十進制計數(shù)的計算機,mod 63 很方便
// 注意:以0 開頭的數(shù)是八進制數(shù)
// 前三項萃取位字段的數(shù)目
// 舉例:如每三位字段全 111B
// (1): 取出高二位對應1 的值, 有兩個1 ,則取出后n 為011
// (3): 取出最高位對應1 的值,有一個1 ,則取出后n 為001
// (2),(4): x - n - n , 剛好11X - 011 - 001 為2+X =高二位1 的數(shù)目+X
// 而最低位X 為1 則自然加1,為0 自然加0
// (5): 合并相鄰的兩個三位字段成六位字段
// (6): mod 63 是怎么操作的呢?
// 假設六位字段值為a6 a5 a4 a3 a2 a1 a0
// 十進制和為a6*64^5 + a5*64^4 + ... + a0
// = a6* (63+1)^5 + a5* (63+1)^4 + ... + a0
// mod 63 剛好剩下a6 + a5 + ... + a0
// 從而把所有六位字段相加,統(tǒng)計1 的總和
int pop3(unsigned int x)
{
#define modu(x, y) (x % 63)
???? unsigned int n;
???? n = (x >> 1) & 033333333333;
???? x = x - n;
???? n = (n >> 1) & 033333333333;
???? x = x - n;
???? x = (x + (x >> 3)) & 030707070707;
???? x = modu(x, 63);
???? return x;
}
?
int pop4(unsigned int x)
{
???? unsigned int n;
?
???? n = (x >> 1) & 0x77777777;
???? x = x - n;
???? n = (n >> 1) & 0x77777777;
???? x = x - n;
???? n = (n >> 1) & 0x77777777;
???? x = x - n;
???? x = (x + (x >> 4)) & 0x0F0F0F0F;
???? x = x * 0x01010101;
???? return x >> 24;
}
?
int pop5(unsigned int x)
{
???? int n;
?
???? n = 0;
???? while (x != 0) {
???????? n += 1;
???????? x = x & (x - 1); // 把最右側的1 位改成0 位
???? }
???? return n;
}
?
int pop6(unsigned int x) {
???? int i, sum;
?
???? sum = x;
???? for (i = 1; i <= 31; i++) {
???????? x = rotate(x, 1);
???????? sum += x;
???? }
???? return -sum;
}
?
?
// 只計算七位量
int pop7(unsigned int x) {
???? x = x * 0x02040810;
???? x = x & 0x11111111;
???? x = x * 0x11111111;
???? x = x >> 28;
???? return x;
}
?
// 只計算八位量
int pop8(unsigned int x) {
???? x = x * 0x08040201;
???? x = x >> 3;
???? x = x & 0x11111111;
???? x = x * 0x11111111;
???? x = x >> 28;
???? return x;
}
?
int pop9(unsigned int x) {
???? int sum;
?
???? sum = x;
???? while (x != 0) {
???????? x = x >> 1;
???????? sum = sum - x;
???? }
???? return sum;
}
?
// table 存放0 到255 的x 的所有pop(x) 值
int pop10(unsigned int x) {
???? static char table[256] = {
???????? 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
???????? 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
???????? 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
???????? 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,??
???????? 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
???????? 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,?
???????? 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,?
???????? 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
???? };
????
???? return table[x???????? ? & 0xFF] +
???????? ?? table[(x >> 8) & 0xFF] +
???????? ?? table[(x >>16) & 0xFF] +
???????? ?? table[(x >>24)];
}
?
//GNU c 的無符號long long 型
/*
int pop64(unsigned int x) {
???? unsigned long long y;
???? y = x * 0x0002000400080010ULL;
???? y = y & 0x1111111111111111ULL;
???? y = y * 0x1111111111111111ULL;
???? y = y >> 60;
???? return y;
}
*/
?
// 15 位量算術版本
const unsigned __int64 a = 0x0002000400080010;
const unsigned __int64 b = 0x1111111111111111;
int pop15(short x) {
???? unsigned __int64 y;
???? y = x * a;
???? y = y & b;
???? y = y * b;
???? y = y >> 60;
???? return y;
}
?
// 生成一個包含4 個8 位部分和的字。然后,盡可能把這些字加起來后,生成一個全字和。
// 相加不會產生溢出的8 位字段的字的數(shù)目是floor(255/8) = 31
int pop_array(unsigned int A[], int n) {
???? int i, j, lim;
???? unsigned int s, s8, x;
?
???? s = 0;
???? for (i = 0; i < n; i += 31) {
???????? lim = __min(n, i + 31);
???????? s8 = 0;
???????? for (j = i; j < lim; j++) {
????????????? x = A[j];
????????????? x = x - ((x >> 1) & 0x55555555);
????????????? x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
????????????? x = (x + (x >> 4)) & 0x0F0F0F0F;
????????????? s8 = s8 + x;
???????? }
???????? x = (s8 & 0x00FF00FF) + ((s8 >> 8) & 0x00FF00FF);
???????? x = (x & 0x0000FFFF) + (x >> 16);
???????? s = s + x;
???? }
???? return s;
}
?
void test5();
void main()
{
???? test5();
}
?
?
void test5()
{
???? srand((unsigned)time(NULL));
???? unsigned int a[256] = {0}; int i;
?
???? for (i = 0; i < 256; i++) {a[i] = i;}
???? cout << pop_array(a, 256) << endl;
?
???? for (i = 0; i < 256; i++) {a[i] = 1;}
???? cout << pop_array(a, 256) << endl;
?
???? for (i = 0; i < 256; i++) {a[i] = 0;}
???? cout << pop_array(a, 256) << endl;
?
???? for (i = 0; i < 256; i++) {a[i] = -1;}
???? cout << pop_array(a, 256) << endl;
?
???? for (i = 0; i < 256; i++) {a[i] = rand()%256;}
???? cout << pop_array(a, 256) << endl;
}
void test4()
{
???? short x;
?
???? x = 0x7FFF; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x0001; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x4000; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x5555; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x2AAA; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x3333; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x2CCC; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x7FFF; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x5B93; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x2C64; hex(x); cout<< dec << pop15(x) << endl;
?
???? x = 0x8000; hex(x); cout<< dec << pop15(x) << endl;
?
}
void test3()
{
???? unsigned char x;
?
???? x = 0x7F; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xFF; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0x0; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xAC; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0x80; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xBD; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0x55; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xAA; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0x0F; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xF0; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0xE4; hex(x); cout<< dec << pop8(x) << endl;
?
???? x = 0x110; hex(0x110); cout << dec << pop7(x) << endl;
}
void test2()
{
???? char x;
?
???? x= 0x7F; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x0; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x0F; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x70; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x55; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x2A; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x5D; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0x33; hex(x); cout << dec << pop7(x) << endl;
?
???? x= 0xF0; hex(x); cout << dec << pop7(x) << endl;
}
void test1()
{
???? int x;
?
???? x = 0x7FFFFFFF; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = -1; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 1; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0xABCDE123; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0x87654321; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0x55555555; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0x80808080; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0xABC5DEFA; hex(x); cout<< dec << pop6(x) << endl;
?
???? x = 0X80000000; hex(x); cout<< dec << pop6(x) << endl;
}
?
?
總結
以上是生活随笔為你收集整理的种群计数 (pop_count)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 键盘扫描表
- 下一篇: Gosper 的序列 循环检测