问题求解:Bitmap中1的个数

  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<iostream>
#include<time.h>

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<num;i++)
            memset(bitmap+rand()%num,rand()%255, sizeof(u64));
     std::cout << "Starting ..." << std::endl;
     u64 t = clock();
     test1(bitmap);
     std::cout << "Test 1: " << clock()-t << "ms" << std::endl;
     t = clock();
     test2(bitmap);
     std::cout << "Test 2: " << clock()-t << "ms" << std::endl;
     delete[] bitmap;
     return 0;
}

void test1(u64* bitmap)
{
     int total = 0;
     u32* bit32;
     u16* bit16;
     u8* bit8;
     u8 temp;
     for(int i = 0; i<num; i++)
     {
           if (bitmap[i] == 0) continue;

           bit32 =  (u32*)&bitmap[i];
           for(int j=0; j<2; j++)
           {
                 if (bit32[j]==0) continue;

                 bit16 =  (u16*)&bit32[j];
                 for(int k=0; k<2; k++)
                 {
                        if (bit16[k]==0) continue;

                        bit8 = (u8*)&bit16[k];
                        for (int m=0; m<2 ;m++)
                        {
                              temp = bit8

­­­­­­­­­­­ ;           

         while(temp)
                              {
                                     if(temp&1)total++;
                                     temp =  temp >> 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值