Bitmap 可以看做是一个连续的内存区域,如int bitmap[n]; 利用每一位的0,1标识一个事物。
碰到这个问题,大多数朋友的解决方案先把bitmap转化char型数组,然后对数组的每
一个元素与1,2,4,8,16,32,64,128 做逻辑与运算,统计结果为真的个数,这样就可以求出
1的个数。
char* bit8 = (char*)bitmap;
u8 a[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
for(int j=0; j<8; j++)
{
if ((bit8[i]&a[j]))
total++;
}
算法平均复杂度为:
数组转化char型后,元素数目: n*4
比较一个元素 : 8
复杂度: n*4*8
如果我们换个思路。
把bitmap看做是一个int数组,如果数组元素值为0,就没有1(节省了32次比较),这样
我们通过类型转换计算转换后的值,可以减少不少比较。而且类型转换相当的快。于是。我们
充分利用类型转换,依次把数组转化成 long long,int,short,char 型,这样我们就能快速的
把0区给去掉。从而减少比较次数。
f f f f f f f f
|-----------64--------------| long long int
|-----32-----| int
|--16--| short
|-8-| char
对于最后的一个char型我们采用移位运算,检测最低位是否唯1,来统计
1的个数。
例如 3 二进制:00000101
00000101 最低位是1,total+1,右移一位
00000010 最低位是0,右移一位
00000001 最低位是1,total+1,右移一位
00000000 总值为0 退出循环, total=2.
算法平均复杂度:(0,1 按x%分布)
数组转化long long 型检查0比较次数 :n*1/2
数组含1的元素个数: (n*1/2) * x%
把剩下的元素转换成int数组,检查比较0的个数比较次数: (n*1/2*x%)*1/2
数组含1的元素个数:(n*1/2*x%*1/2)* x%
把剩下的元素转换成short数组,检查比较0的个数比较次数:(n*1/2*x%*1/2*x%)*1/2
数组含1的元素个数:(n*1/2*x%*1/2*x%*1/2)* x%
把剩下的元素转换成char数组,检查比较0的个数比较次数: (n*1/2*x%*1/2*x%*1/2*x%)*1/2
数组含1的元素个数:(n*1/2*x%*1/2*x%*1/2*x%*1/2)* x%
char数组元素移位运算比较次数:(n*1/2*x%*1/2*x%*1/2*x%*1/2*x%)*8/2
求和:n*1/2 + n*1/4*x% + n*1/8*x%^2 + n*1/16*x%^3+n*1/4*x%^4 = n*1/2 * (1+ x%)(近似值)
这里给出最后1个字节的一种优化算法:
若计算二进制 10101011 中1的个数。
| 1 0 | 1 0 | 1 0 | 11 | 每两位一组 ,计算每组1个个数,将结果存在每组中,可求得
| 0 1 | 0 1 | 0 1 | 1 0| 每两组合并成新组,计算一的个数,将结果存在每组中,可求得
| 0 0 1 0 | 0 0 1 1| 每两组合并成新组,计算一的个数,将结果存在每组中,可求得
| 0 0 0 0 0 1 0 0| 最终的这个数就是1个个数,结果为4.
这个算法类似归并排序, 算法复杂度log[2]N ,8位需要3次计算,64位仅需要4次,
但是由于计算机字长限制,一次最多处理64位,提升效率有限。
具体程序实现:
char temp = 10101011 ;
temp = (temp&0x55) + ((temp>>1)&0x55);
temp = (temp&0x33) + ((temp>>2)&0x33);
temp = (temp&0x0f) + ((temp>>4)&0x0f);
最后temp值为1的个数。
如果大家有更好的方法欢迎指教,谢谢大家。
附程序代码及运行效果:
//============================================================================
// Name : MyTester.cpp
// Author : Ice
// Version :
// Copyright : Your copyright notice
// Description : Count numbers of one in bitmap, C++, Ansi-style
//============================================================================
#include
#include
typedef unsigned long long u64;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
const int num = 100000000;
//const int num = 1;
void test1(u64*);
void test2(u64*);
int main()
{
u64* bitmap = new u64[num];
//随机设置内存 0 ,1
for (int i= rand()%num; i> 1;
}
}
}
}
}
std::cout << "total " << total << std::endl;
}
void test2(u64* bitmap)
{
int total = 0;
u8* bit8 = (u8*)bitmap;
u8 a[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
for(int i = 0; i<(int)(num*sizeof(u64)); i++)
{
for(int j=0; j<8; j++)
{
if ((bit8[i]&a[j]))
total++;
}
}
std::cout << "total " << total << std::endl;
}
运行效果:检测800M内存运行时间。
Starting ...
total 1043624
Test 1: 921ms
total 1043624
Test 2: 25990ms