题目要求:若二进制无符号整数x包含偶数个1,返回1,否则返回0.
要求:只能使用位运算、加减法和==、!=,最多包含12个算术运算、位运算和逻辑运算,可以假设sizeof(unsigned)==4
不能使用乘除模运算,不能使用条件分支,循环,函数调用,大小比较等(详见《深入理解计算机系统》第二章习题)
首先,最朴素最直接的方法——一位一位统计肯定次数远远多于12次.
考虑:异或运算(^)可以保持两个操作数1的个数的奇偶性.
因为两个操作数,一1一0,结果为1,1的个数还是奇数;两0,结果为0,不影响,两1,结果为0,1的个数还是偶数.
这样,想到了降量的思想,把判断32位数转化为16位,然后依次进行.
先开始想的是分别取到前/后16位,然后异或:
1.x&0xFFFF0000;
2.x&0x0000FFFF;
3.将步骤1得到的结果向右移16位;
4.将2和3的结果异或.
4得到的结果和原32位数1的个数的奇偶性一致.
但是需要4*5+1(根据要求取反)=21步.
简化一下,1和3可以通过x/65536这一步得到,这样,需要步数是3*5+1=16步,还是超出了要求.
仔细想想,将x和x/65536异或,得到结果前16位和原x前16一致,但是后16位1的个数奇偶性与原x二进制1的个数奇偶性一致.
这样,两步完成32位到16位的简化,2*5=10次之后,得到结果前面31位不用管,最后1位说明了奇偶性.
这时,把结果模2,得到最后1位,0或者1.
然后取反,得到题目要求的结果.总共需要12步.
用C++(底层数据的表示和C一样)写了一段程序并简单测试了一下.
下面附上源码,一共使用6次算术运算,5次位运算和1次逻辑运算.
代码如下:
nt even_ones(unsigned x ) { unsigned x16 = (x>>16)^x; unsigned x8 = (x16>>8)^x16; unsigned x4 = (x8>>4)^x8; unsigned x2 = (x4>>2)^x4; unsigned x1 = (x2>>1)^x2; return (~x1)&0x1; } int main() { unsigned a; scanf("%u",&a); int ret=even_ones(a); printf("%d",ret); }
参考:http://blog.csdn.net/zhzhanp/article/details/6324511