关于位操作的一点感想

前段时间看关于位的操作,一直想写点东西,后来又被问到关于码距的问题,发现好多基础性的东西其实是在高级的应用中用到的最多的东西,并且在应用中有很客观的效率。

遇到过这样一个问题,一个32位二进制数,再给出一个有大量32位二进制数的集合,求解集合中与给定的数不同位数在3以内的数。

当时的第一反应是,逐个遍历,把集合中的数与给定的数进行异或运算,统计结果中为“1”的位数即可。逐个遍历,进行异或运算,这两步操作暂时没有想到太大的优化空降,看过一些材料,尤其是斯坦福大学的一片文章:Bit Twiddling Hacks之后发现原来可以有很多优化的方式。


最简单最直接的思路:

移位+模运算+计数器统计的方式:

while(a!=0){
    if(a%2 == 1)
        count++;
    a=a>>1;
}
即,每次判断末尾是不是“1”,如果是1则计数器+1,判断完后右移一位,直到数为零。

空间换时间的思路:

在空间需求比较容易满足,但是时间紧迫性要求较高的情况下,采用空间换时间的方式来做:

unsigned short int count[4294967296];
    count[0]=0;
    count[1]=1;
    count[2]=1;
    ...
    count[4294967295]=32;

直接通过查表的方式,将取得的二进制数转换为数组的下标即可。但是这样的内存消耗也是很大的:

2^32=4G,每个unsigned short int 占16 bits即 2字节,所以数组要占用8GBytes的空间,目前主流的单个服务器配置也基本上在这个水平。在这种情况下,比较适合做有大量的待处理数据,并且在真正要处理的时候,可以用hash的方式将数据分块,不同的数据分散到不同的节点上,每个节点之负责本区域内的数据,如用8台机器,每台只负责1G数据的运算,就相对处于一个合理的区间内(觉得此处还有很大的可以讨论的空间)。

很巧妙的思路:

在斯坦福的文章中,给出了一个很巧妙的思路:

int count(int n)
{
    n = (n&0x55555555)+((n>>1)&0x55555555);
    n = (n&0x33333333)+((n>>2)&0x33333333);
    n = (n&0x0f0f0f0f)+((n>>4)&0x0f0f0f0f);
    n = (n&0x00ff00ff)+((n>>8)&0x00ff00ff);
    n = (n&0x0000ffff)+((n>>16)&0x0000ffff);
 
    return n;
}

这种方式,设置了一系列二进制位数,像一个上三角形的筛子一样,把n中的数都筛选到合适的位置上,同时利用了二进制位的“权重”信息,即两个不同位的“1”最后同时映射到了2^1上,四个不同位置的“1”最后则映射到2^2上。设计相当巧妙,空间复杂度不高,只需一个定额的数组,对于每个需要处理的二进制数,计算上的时间复杂度也在log(二进制数长度)的数量级上。

其实,巧妙的设计可以有效减少时空复杂度,并不一定要牺牲了空间来换取时间上的性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值