线性概率计数器,用于基数估计。所谓基数,就是数据中不重复的数据有多少个。例如共有N个数据,开辟一片长度为M的bitmap,清零,对N个数据进行遍历,每个数据
取哈希,再对M取模,得到的值为i,于是将bitmap中第i个元素置位为1,最后统计bitmap中0的比例rate,计算-M*ln(rate)的值,即为N个数据中基数的估计值。
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define BIG 100000 // BIG个整数
#define SM 2000 // SM个unsigned char,作为bitmap
typedef unsigned int uint;
typedef unsigned char uchar;
uint ai[BIG]; // 计算的数据
uchar uclist[SM]; // 1000*8 //bitmap
void SetBit(uchar *p, uint n) // 对bitmap p的第n个位置1
{
uint n1=n/8;
uint n2=n%8;
p[n1] = p[n1] | (1<<n2);
}
uint hash( uint a) // 整数哈希函数,别的地方抄来的。用好的哈希函数很重要,效果比直接取模好的多
{
a = (a+0x7ed55d16) + (a<<12);
a = (a^0xc761c23c) ^ (a>>19);
a = (a+0x165667b1) + (a<<5);
a = (a+0xd3a2646c) ^ (a<<9);
a = (a+0xfd7046c5) + (a<<3); // <<和 +的组合是可逆的
a = (a^0xb55a4f09) ^ (a>>16);
return a;
}
uint GetBit(uchar *p, uint n) // 取得bitmap第n个位的值
{
uint n1=n/8;
uint n2=n%8;
uchar ta=p[n1];
ta = (ta>>n2)&0x01;
return ta;
}
uint count(uint *ip, uint ni, uchar *cp, uint nc) // nc为数组大小,概率计数器,返回的是bitmap中1的个数
{
uint i;
for (i=0; i<ni; i++)
{
SetBit(cp, (hash(ip[i]))%(nc*8));
}
uint s=0;
for (i=0; i<nc*8; i++)
{
s += GetBit(cp,i);
}
return s;
}
// 测试
int main()
{
srand((uint)time(NULL)); //随机数种子
uint i;
for (i=0; i<BIG; i++)
{
ai[i]=3*i;
}
for (i=0; i<SM; i++)
{
uclist[i]=0;
}
uint s = count(ai, BIG, uclist, SM);
double f = double(SM*8-s)/double(SM*8);
f = -8.0*SM*log( f);
printf("bitmap size: %d\n bitmap 1 的数量: %d\n估计基数: %f \n实际基数:%d\n (错误率:%f)\n", 8*SM, s, f, BIG,abs(f-BIG)/BIG);
return 0;
}
运行截图:
SM=2000
SM=4000
SM=6000