统计bytearray中的bitcount

Posted weiyinfu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了统计bytearray中的bitcount相关的知识,希望对你有一定的参考价值。

给定一个byte数组,要求统计byte数组的bitcount,也就是byte数组中为1的位的个数。

Redis提供了位数组数据结构,位数组是相对独立的一个程序,在《Redis设计与实现》(黄建宏著)一书中,对此有详细介绍。

回到问题上来,看到这个问题的第一印象就是暴力。假设byte数组长度为n,那么时间复杂度为8n。
我们的目的就是优化常数。

很容易想到的是用空间换时间,预先打表0~255之间的全部数字的bitcount,用到时直接查表,复杂度将为O(n).
这种方法的缺点在于只能存储8位而不能存储太多,因为随着位数的增加,空间消耗指数级增长。

最高级的一种优化方法建立在位运算基础上。这种方法的思想基于分治算法。
首先两两一组分成16组,然后四四一组分成8组,八八一组分成4组,16、16一组分成两组。
统计各个组内1的个数,然后归并可以通过移位运算和加法运算实现。

#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;
int bitcountBruteForce(char*a,int len){
    int s=0;
    for(int i=0;i<len;i++){
        for(int j=0;j<8;j++){
            if(a[i]&(1<<j)){
                s++;
            }
        }
    }
    return s;
}
int table[1<<8];
int bitcountTable(char*a,int len){
    int s=0;
    for(int i=0;i<len;i++){ 
        s+=table[int(a[i]&0xff)];
    }
    return s;
}
int swar(int x){
    x=(x&0x55555555)+((x>>1)&0x55555555);
    x=(x&0x33333333)+((x>>2)&0x33333333);
    x=(x&0x0f0f0f0f)+((x>>4)&0x0f0f0f0f);
    x=(x*0x01010101)>>24;
    return x;
}
int swar2(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);//x=(x>>16)+(x&31);
    return x;
}
int bitcountSWAR(char*a,int len){
    int s=0;
    int intLen=len/4;
    int left=len%4;
    int*ia=(int*)a;
    for(int i=0;i<intLen;i++){
        s+=swar2(*ia);
        ia++;
    }
    for(int i=intLen*4;i<len;i++){
        s+=table[a[i]];
    }
    return s;
}
void initTable(){
    for(int i=0;i<(1<<8);i++){
        int s=0;
        for(int j=0;j<8;j++){
            if(i&(1<<j)){
                s++;
            }
        }
        table[i]=s;
    }
}
int timeit(int(*f)(char*,int),char*a,int len,int manytimes){
    int begtime=time(0);
    int res=0;
    for(int i=0;i<manytimes;i++){
        res=f(a,len);
    }
    cout<<"ans "<<res<<endl;
    int endtime=time(0);
    int timeused=endtime-begtime;
    return timeused;
}
int main(){
    const int n=1e6;
    int a[n];
    initTable();
    srand(time(0));
    for(int i=0;i<n;i++){
        a[i]=rand();
    }
    cout<<"brute force "<<timeit(bitcountBruteForce,(char*)a,n*sizeof(int),10)<<endl;
    cout<<"table method "<<timeit(bitcountTable,(char*)a,n*sizeof(int),10)<<endl;
    cout<<"swar method "<<timeit(bitcountSWAR,(char*)a,n*sizeof(int),10)<<endl;
    return 0;
}

以上是关于统计bytearray中的bitcount的主要内容,如果未能解决你的问题,请参考以下文章

JDK源码之Integer类—bitCount()方法

328请教一个Redis bitmap做签到功能的问题。 统计近7天连续签到用户数,bitop+bitcount是怎么用的?

c_cpp 快速代码片段,用于在统计(阻止)/ dev / rdsk中的设备时验证fstat64和stat64的行为。

Java源码解释之Integer.bitCount

如何在炼金术中将 ByteArray 传递给 C 代码?

redis的setbit这个bit怎么理解,配合bitcount使用