位运算及应用

位运算及应用《一》

1.位运算功能简介

操作符功能
<<(shl)操作数左移,相当于乘以2 ˆ k
>>(shr)操作数右移,相当于除以2 ˆ k
>>>无符号右移,左边空出的位一概补0
&(and)某数二进制特定位置0,取某数二进制指定位
|某数二进制特定位置1
ˆ某数二进制特定位取反
~某数二进制取反

注:不能使用带符号右移位来实现带符号整数除以2的幂的运算,如果被除数是负数,而且有非零位被移出,那么需要在移位结果上加一

2.位运算技巧

功能实例位运算
去掉最后一位01011000=>00101100x >> 1
在最后加一个001011000=>010110000x << 1
在最后加一个101011000=>010110001x << 1 + 1
析出最右侧的1①01011000=>00001000x & (-x)
析出最右侧的0②10100111=>00001000~x & (x + 1)
识别后缀0的掩码③01011000=>00000111~x & (x - 1),或 ~(x | -x),或 (x & -x) - 1
识别最右侧1和后缀0的掩码④01011000=>00001111x ˆ (x - 1)
把右侧尾部连续的0变成101011000=>01011111x | (x - 1)
把右侧尾部连续的1变成001010111=>01010000x & (x + 1)
把右侧连续的1位串改为0位串⑤01011000=>01000000((x | (x - 1)) + 1) & x
把最后一位变成101011000=>01011001x | 1
把最后一位变成001011001=>01011000(x | 1)-1
最后一位取反01011001=>01011000x ˆ 1
把右数第k位变成101011001=>01111001(k = 6)x | (1 << (k - 1))
把右数第k位变成001011001=>010011001(k = 5)x & ~(1 << (k - 1))
右数第k位取反01011001=>01111001(k = 6)x ˆ (1 << (k - 1))
取末k位01101101->00001101(k = 5)x & (1 << k - 1)
取右数第k位01101101=>1(k = 4)(x >> (k - 1)) & 1
把末k位变成100101001->00101111(k = 4)x | ((1 << k) - 1)
末k位取反01011001=>01010110(k = 4)x ˆ ((1 >> k) - 1)
把右起第一个0变成101011001=>01011011x | (x + 1)
取右边连续的110101111=>00001111(x ˆ (x + 1)) << 1
x循环左移k次10101001=>10011010(k = 4)(x << k) | (x << (32 - k))
x循环右移k次10101001=>00110101(k = 3)(x >> k) | (x >> (32 - k))
32位整数高16位和低16位交换0xFFFFAAAA=>0xAAAAFFFF(x << 16)
去掉右起第一个1的左边⑥10101000=>00001000x & (x ˆ (x - 1))

**注: ①如果没有1位则生成所有位均为0的字
   ②如果没有0位则生成所有位均为0的字
   ③如果x = 0则生成所有位都为1的字其中第一个公式具有指令级并行的性质
   ④如果所有位都为0则生成所有位都为1的字
   ⑤对于j>k>0,对这一公式的运算结果做0检测可以用来检查一个非负整数是否具有
    2^j - 2^k的形式。
   ⑥最后这一个在树状数组中会用到。

3.二进制补码公式

-x = ~x + 1 = ~(x - 1)
~x = -x - 1
-(~x) = x + 1
~(-x) = x - 1
x + y = x - ~y - 1 = (x | y) + (x & y)
x - y = x + ~y + 1 = (x | ~y)-(~x & y)
x ^ y = (x | y) - (x & y)
x | y = (x & ~y) + y
x & y = (~x | y) - ~x
x == y = ~((x - y) | (y - x))
x != y = (x - y) | (y - x)
x < y = (x - y) ^ ((x ^ y) & ((x - y) ^ x))
x <= y = (x | ~y) & ((x ^ y) | ~(y - x))
x < y = (~x & y) | ((~x | y) & (x - y))//无符号x,y比较
x <= y = (~x | y) & ((x ^ y) | ~(y - x))//无符号x,y比较

4.位运算的实际应用

1.实现swap()函数
void swap(long long& a,long long& b){
a ^= b ^= a ^= b;
}
2.位运算小技巧

①条件语句替换

if(x != a)
 x = b;
 可替换为 x = a ^ b ^ x;

②判断一个无符号数是不是2的幂

bool IsTwoExp(unsigned int x){
return(((x & (x - 1)) == 0) && (x != 0));
}

③求Gray码的第n个数

unsigned int GetNGray(unsigned int n){
return (n ^ (n >> 1));
}

④判断一个无符号数是不是2的幂减一

bool IsTwoExpDec(unsigned int x){
return ((x & (x + 1)) == 0);
}

⑤判断奇偶数

bool IsOdd(int x){
return ((x & 1) == 1);
}
bool IsEven(int x){
return ((x & 1) == 0);
}

⑥求两个数的平均值

int GetAverage(int x,int y){
return (x & y + ((x ^ y) >> 1));
}

⑦求32整数的绝对值

unsiged int abs(int x){
int y = (x >> 31);
return (x ^ y) - y;
}
unsiged int abs(int x){
int y = (x >> 31);
return (x + y) ^ y;
}  
unsiged int abs(int x){
int y = (x >> 31);
return (x ^ (-y)) + y;
} 

⑧对2^k取模

unsigned int mod(unsigned int x,int k){
return x & ((1 << k) - 1);
}
3.统计某数二进制中1的数目

代码1

int count_1(int x){
int count = 0;
while(x != 0){
x &= (x - 1);
count++;
}
return count;
}

代码2

int count_1(int x){
x ^= (x >> 1);
x ^= (x >> 2);
x ^= (x >> 4);
x ^= (x >> 8);
x ^= (x >> 16);
return (x & 1);
}

代码3

int count_1(int x){
x = (x & 0x55555555) + ((x >> 1) & 0x55555555); 
x = (x & 0x33333333) + ((x >> 2) & 0x33333333); 
x = (x & 0x0F0F0F0F) +  ((x >> 4) & 0x0F0F0F0F); 
x = (x & 0x00FF00FF) +  ((x >> 8) & 0x00FF00FF); 
x = (x & 0x0000FFFF) +  ((x >> 16)& 0x0000FFFF);
return x;
} 
3.统计某数二进制前导0的数目
int pre_0(unsigned x)
{
   if (x == 0) return(32);
   int n = 1;
   if ((x >> 16) == 0) {n = n +16; x = x <<16;}
   if ((x >> 24) == 0) {n = n + 8; x = x << 8;}
   if ((x >> 28) == 0) {n = n + 4; x = x << 4;}
   if ((x >> 30) == 0) {n = n + 2; x = x << 2;}
   n = n - (x >> 31);
   return n;
}
5.32位数二进制逆序输出
int reverse(int x){
  x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1);
  x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2);
  x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4);
  x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8);
  x = ((x & 0x0000FFFF) << 16)| ((x & 0xFFFF0000) >> 16);
 return x;
 }
6.0到N这N + 1个数二进制1的个数总和

原题

int count_sum_1(int n,int m) 
{
int tmp1 = n >> m;
int tmp2 = 1 << m; 
if(tmp1 == 0) 
   return 0;  
if((tmp1 & 1) == 1)
return tmp2 * tmp2 + (n & (tmp2 - 1)) + 1 + count_sum_1(n,m+1);  
    else 
    return (n >>(m + 1)) * tmp2 + count_sum_1(n,m+1);  
 }  
n皇后问题位运算版
初始化 upperlim:=(1 shl n)-1
procedure test(row,ld,rd:longint);
var
      pos,p:longint;
begin

  if row<>upperlim then
  begin
  pos:=upperlim and not (row or ld or rd);
  while pos<>0 do    
  begin
  p:=pos and -pos;
  dec(pos,p);
  test(row+p,(ld+p)shl 1,(rd+p)shr 1);
  end;
  end;
  else inc(sum);
end.
Gray码问题

原题

var
   x,y,m,n,u:longint;
begin
   readln(m,n);
   for x:=0 to 1 shl m-1 do begin
    u:=(x xor (x shr 1)) shl n;//输出数的左边是一个m位的Gray码
      for y:=0 to 1 shl n-1 do
       write(u or (y xor (y shr 1)),' ');//并上一个n位Gray码
      writeln;
   end;
end.

注:本文1,3小节摘录自超级大笨狼的博客
本文2,4小节摘录自Matrix67的博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值