编程之美读书笔记:求一个字节(8bit)的无符号整型变量表示的二进制中1的个数
2012-05-07 21:43
288 查看
#include <iostream> using namespace std; typedef unsigned char BYTE; // Reader:shizhixin // Email:szhixin@gmail.com // Blog:http://blog.csdn.net/shizhixin // Date:2012-05-07 // Function:求一个字节(8bit)的无符号整型变量表示的二进制中1的个数 // Note: 要求算法执行效率尽可能高。(题目来自于编程之美2.1,程序参考编程之美) // Example: //通过右移和与操作获取字节中1的个数 int getnumber1(BYTE num) { int count=0; for (int i=0; i<8; i++) { if (((num>>i) & 0x01) == 1) { count++; } } return count; } //在1的基础上进行代码的修改,算法一样,相对于1中,把if改为while, //优点是考虑到右移到最后肯定为0,可以做循环的结束;另外与操作后 //的数不是1就是0,所以无需if判断是否增加计数 //复杂度log(num) int getnumber2(BYTE num) { int count=0; while(num) { count += num & 0x01; num >>= 1; } return count; } //用到一个巧妙的与操作,v & (v -1 )每次能消去二进制表示中最后一位1, //利用这个技巧可以减少一定的循环次数。 int getnumber3(BYTE num) { int count=0; while(num) { num &= num-1; count++; } return count; } //空间换时间,但是不适合位数更大的操作,比如DWORD int getnumber4(BYTE num) { int lookTable[256]={ 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 }; return lookTable[num]; } void main() { int num; cout<<"please input a BYTE [0-255]"<<endl; cin>>num; while (num < 0 || num > 255) { cout<<"please input a BYTE [0-255]"<<endl; cin>>num; } cout<<"getnumber1 is :"<<getnumber1((BYTE)num)<<endl; cout<<"getnumber2 is :"<<getnumber2((BYTE)num)<<endl; cout<<"getnumber3 is :"<<getnumber3((BYTE)num)<<endl; cout<<"getnumber4 is :"<<getnumber4((BYTE)num)<<endl; }
----------------------------------------------------------------------------------------------------------------------------------------------------
还有更好的解法:《编程之美》读书笔记——“求二进制数中1的个数”
/article/2619210.html
编程之美——微软技术面试心得》读书笔记
“求二进制数中1的个数”
by ZelluX
由电子工业出版社博文视点和w3china.org社区联合举办了“看样章, 写书评, 赢取《编程之美——微软技术面试心得》的活动”,详情请见:http://bbs.w3china.org/dispbbs.asp?boardID=42&ID=61162
大家踊跃参加,活动非常热烈。下面文章来自读者ZelluX:
求二进制中1的个数。对于一个字节(8bit)的变量,求其二进制表示中"1"的个数,要求算法的执行效率尽可能的高。
先来看看样章上给出的几个算法:
解法一,每次除二,看是否为奇数,是的话就累计加一,最后这个结果就是二进制表示中1的个数。
解法二,同样用到一个循环,只是里面的操作用位移操作简化了。
1: int Count(int v)
2: {
3: int num = 0;
4: while (v) {
5: num += v & 0x01;
6: v >>= 1;
7: }
8: return num;
9: }
解法三,用到一个巧妙的与操作,v & (v -1 )每次能消去二进制表示中最后一位1,利用这个技巧可以减少一定的循环次数。
解法四,查表法,因为只有数据8bit,直接建一张表,包含各个数中1的个数,然后查表就行。复杂度O(1)。
1: int countTable[256] = { 0, 1, 1, 2, 1, ..., 7, 7, 8 };
2:
3: int Count(int v) {
4: return countTable[v];
5: }
好了,这就是样章上给出的四种方案,下面谈谈我的看法。
首先是对算法的衡量上,复杂度真的是唯一的标准吗?尤其对于这种数据规模给定,而且很小的情况下,复杂度其实是个比较次要的因素。
查表法的复杂度为O(1),我用解法一,循环八次固定,复杂度也是O(1)。至于数据规模变大,变成32位整型,那查表法自然也不合适了。
其次,我觉得既然是这样一个很小的操作,衡量的尺度也必然要小,CPU时钟周期可以作为一个参考。
解法一里有若干次整数加法,若干次整数除法(一般的编译器都能把它优化成位移),还有几个循环分支判断,几个奇偶性判断(这个比较耗时间,根据CSAPP上的数据,一般一个branch penalty得耗掉14个左右的cycle),加起来大概几十个cycle吧。
再看解法四,查表法看似一次地址计算就能解决,但实际上这里用到一个访存操作,而且第一次访存的时候很有可能那个数组不在cache里,这样一个cache miss导致的后果可能就是耗去几十甚至上百个cycle(因为要访问内存)。所以对于这种“小操作”,这个算法的性能其实是很差的。
这里我再推荐几个解决这个问题的算法,以32位无符号整型为例。
1: int Count(unsigned x) {
2: x = x - ((x >> 1) & 0x55555555);
3: x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
4: x = (x + (x >> 4)) & 0x0F0F0F0F;
5: x = x + (x >> 8);
6: x = x + (x >> 16);
7: return x & 0x0000003F;
8: }
这里用的是二分法,两两一组相加,之后四个四个一组相加,接着八个八个,最后就得到各位之和了。
还有一个更巧妙的HAKMEM算法
1: int Count(unsigned x) {
2: unsigned n;
3:
4: n = (x >> 1) & 033333333333;
5: x = x - n;
6: n = (n >> 1) & 033333333333;
7: x = x - n;
8: x = (x + (x >> 3)) & 030707070707;
9: x = modu(x, 63);
10: return x;
11: }
首先是将二进制各位三个一组,求出每组中1的个数,然后相邻两组归并,得到六个一组的1的个数,最后很巧妙的用除63取余得到了结果。
因为2^6 = 64,也就是说 x_0 + x_1 * 64 + x_2 * 64 * 64 = x_0 + x_1 + x_2 (mod 63),这里的等号表示同余。
这个程序只需要十条左右指令,而且不访存,速度很快。
由此可见,衡量一个算法实际效果不单要看复杂度,还要结合其他情况具体分析。
关于后面的两道扩展问题,问题一是问32位整型如何处理,这个上面已经讲了。
问题二是给定两个整数A和B,问A和B有多少位是不同的。
这个问题其实就是数1问题多了一个步骤,只要先算出A和B的异或结果,然后求这个值中1的个数就行了。
总体看来这本书还是很不错的,比较喜欢里面针对一个问题提出不同算法并不断改进的风格。这里提出一点个人的理解,望大家指正 ;-)
相关文章推荐
- 一个字节(8bit)的无符号整形变量,求二进制表示中“1”的个数。
- 对于一个字节(8bit)的变量,求二进制表示中"1”的个数,要求算法的执行效率尽可能的高
- 对于一个字节(8bit)的变量,求其二进制表示中"1"的个数,要求算法的执行效率尽可能地高。
- 对于一个字节(8bit)的无符号整形变量,求二进制表示中“1”的个数,要求算法执行效率尽可能地高
- 转:对于一个字节(8bit)的变量,求其二进制表示中“1”的个数
- 对于一个字节(8bits)的变量,求其二进制表示中“1”的个数
- C语言中一个字符对应一个ascii码;占一个1个字节8个二进制位;存到内存中也是用ascii的十进制的二进制表示
- 用一个双字节变量表示两个单字节组成的双字节数据
- 将一个int类型变量(4字节), 以二进制形式进行输出--showbits.c
- 求一个字节(8bit)的无符号整形变量,其二进制中1的个数。
- 统计一个自然数的二进制表示形式中有多少个1
- VB中如何将两个单字节合成一个双字节,例如我输入两个byte类型变量
- 二进制位,字节,字长计算机内表示,及二进制、八进制、十六进制
- 统计一个整型数组所有元素二进制表示中1的个数
- 将一个正整数N用二进制表示并转化为一个string类型的值s
- 计算一个整数的二进制表示有多少个1(别人的最快算法)
- 逆转一个整数的二进制表示问题
- 编程之美1——一个数的二进制表示中1的个数
- 用二进制打印一个字节
- 2.2题目:输入一个整数a,再输入两个整数p1,p2(p1,p2<32),输出该整数的二进制表示方法中从右端开始的p1到p2位.