二进制数中1的个数

作者: | 更新日期:

二进制中1的个数有很多方法, 最后有些acm题为了考察这点, 更是把时间卡到必须用O(1)才能过的地步, 现在我们来看看这些算法. 最后再赠送一个你绝对没有见过的高效的算法.

![cover][]

前言

如果你是 acmer 的话, 可能说这个太简单了. 但是我最后写一个算法你肯定没有见过, 且复杂度O(1), 并且没有用空间换时间.
如果你是 acmer 的话, 顺便可以做做这道题. [codeforces 472 比赛 G 题][codeforces-472G] 如果你是 acmer 或想自己证明下面算法的话, 就看下面的文章, 其他人可以绕道看这篇文章[详解二进制数中1的个数][bit-count-more]

若干算法

这里假设计算的是 32位无符号整数吧.

暴力

最暴力的算法就是一位一位的判断, 复杂度O(32).

unsigned __countbits(unsigned x){
    unsigned n=0;
    while(n +=(x&1) , x >>= 1);
    return n;
}

与1的个数有关

这个复杂度不好说, 因为复杂度就是1的个数, 而1的个数是不确定的.

基本原理是 x & (x-1) 可以把x的最低位 1置换成0.

unsigned _countbits(unsigned x){
    unsigned n=0;
    while(x && (++n , x&=x-1));
    return n;
}

分治法

分治法的原理可以看这篇文章最上面的图片.
对于32 位整数, 需要至少5次才能算出来.
所以复杂度是 O(n).

uint countbits(uint x) {
    uint mask[]= {0x55555555,0x33333333, 0x0F0F0F0F,
                  0x00FF00FF, 0x0000FFFF
                 };
    for(uint i=0,j=1; i<5; i++,j<<=1) {
        x=(x & mask[i]) + ((x>>j) & mask[i]);
    }
    return x;
}

打表法

计算机的世界里, 大家应该牢记:时间与空间永远是矛盾的, 当你时间足够的时候, 就可以去考虑能不能用空间换取时间.

但是32位整数太大, 空间太大, 怎么办呢?
于是有人发现我们可以打16位的表, 这样合起来就是32位了.
于是这个方法就出现了.

const int OneNumMax = 1<<16;
int oneNum[OneNumMax];

int init(){
    for(int i = 1; i < OneNumMax; ++i) {
        oneNum[i] = oneNum[i >> 1] + (i & 1);
    }
    return 0;
}

unsigned countbits(unsigned x) {
    static int a = init();
    return oneNum[x >> 16] + oneNum[x & ((1 << 16) - 1)];
}

你想不到的算法

很多人看到这也就没有其他方法了.

先看代码

int bitCount ( unsigned n ) {
    unsigned tmp = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
    return ( (tmp + (tmp >> 3) ) & 030707070707) % 63;
}

看完了自己想吧, 周末了, 我的笔记本还没有修好, 周末只能玩手机, 没时间写了, 下周一补上说明吧.

详细的讲解见这里 [详解二进制数中1的个数][bit-count-more]

<完> [bit-count-more]: http://github.tiankonguse.com/blog/2014/11/16/bit-count-more/ [codeforces-472G]: http://github.tiankonguse.com/blog/2014/10/04/codeforces-472G/ [cover]: http://tiankonguse.com/lab/cloudLink/baidupan.php?url=/1915453531/3526593306.png
点击查看评论

关注公众号,接收最新消息

关注小密圈,学习各种算法

tiankonguse +
穿越