位运算相关内容整理

位运算相关内容整理

1负数

负数的右移:负数右移的话,由于要保持它是负数,所以负数的二进制的左边补1。如果一直右移的话,最后就就变成0xFFFFFFFF 即-1

如: -4>>1 为-2 ;-4>>2为-1

负数的左移:跟正整数左移一样,右边补0。左移总是在低位补零,高位丢失,因而负数左移后可能会变成正数。

int x = 0x8fff0000;

cout << (x << 1); // 输出为536739840

cout << (-2 << 31); // 输出为0

 

2)位运算n&(n-1)的妙用

i) 判断一个数是否是2的方幂 –-

n>0 && ((n&&(n-1)) == 0)

ii) 求某一个数的二进制表示中1的个数 --

    这里实际上是一种数字分析:把一个整数减去1,再和原整数做与运算,会把该整数最右边的一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。

/*fun返回值是参数n转化为二进制后包含1的数量
要注意,此例中形参的unsigned不可省略。
这是因为while循环里有n>0的比较操作,若将形参声明为int,则负数无法得到结果
【将int转化为unsigned int,并不会改变内存中数据的存储内容,只是解释不同】*/
int fun(unsigned int n) {
    int cnt = 0;
    while (n > 0) {
        n &= (n - 1);
        cnt++;
    }
    return cnt;
}

/*或者可以写成如下形式*/
// -- 推荐吧!!
int fun2(int n) {
    int cnt = 0;
    while (n != 0) {
        n &= (n - 1);
        cnt++;
    }
    return cnt;
}

 

    另外,可以推广至:1)判断一个整数是不是2的整数次方。——if(n&(n-1)==0);2)两个整数m和n,计算需要改变多少次m的二进制表示,才能够得到n(先异或,再求异或的结果二进制表示中1的个数)。

iii) 计算N!的质因数2的个数

容易得出N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + ....

下面通过一个简单的例子来推导一下过程:N = 10101(二进制表示)

现在我们跟踪最高位的1,不考虑其他位,假定都为0,

[N / 2]    01000

[N / 4]    00100

[N / 8]    00010

[N / 16]    00001

则所有相加等于01111 = 10000 - 1

由此推及其他位可得:(10101)!的质因数2的个数为10000 - 1 + 00100 - 1 + 00001 - 1 = 10101 - 3(二进制表示中1的个数)

推及一般N!的质因数2的个数为N-(N二进制表示中1的个数)

 

3)关于异或运算符。

i)

异或运算符有一个奇特的性质:两个相同的数异或之后结果为0,且满足交换律。即:若有A^B^C^D^E^F^B,等价于A^C^D^E^F。

这一性质常用于寻找成对出现时缺失的某一个数。

例题:给你一个由n-1个整数组成的为排序的序列,其元素都是1-n中的不同整数。请写出寻找序列中缺失整数的线性时间算法。

解答:若用n个数的和减去n-1个数的和,可能有溢出。

因此,用异或方法。首先求得n个数的异或结果,在用题中所给的序列与之前的结果求异或。最终得到的就是丢失的整数。

ii)不使用第三方变量交换两个整数a和b。

a=a^b;

b=a^b;

a=a^b;

 

iii)不用加减乘除做加法

int add_no_op(int num1, int num2) {
    if (num2 == 0)
        return num1;
    int sum = num1^num2;
    int carry = (num1&num2) << 1;
    return add_no_op(sum, carry);
}

iv)用位运算操作求两个数的均值

(x&y)+(x^y)>>2;

posted on 2017-07-09 14:15  sjqiu  阅读(188)  评论(0编辑  收藏  举报

导航