关闭

位操作整理

590人阅读 评论(0) 收藏 举报
分类:

整理一下位操作,便于自己以后查看。例子全部来源于网络,不能一一指出出处,见谅!注意:以下的例子都是一个具体的题目,有很多的时候并不意味着像下面那样做得到的代码更加高效,应该具体问题具体分析。

1.异或操作

定义:

异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b).

0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0。同0异1.与其它语言不同,C语言和C++语言的异或不用xor,而是“^”,其他语言的"^"多表示乘方。

运算法则

(1).a ⊕ b = b ⊕ a;//交换率

(2)a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;//结合率

异或的特性:

(1).0异或任何数=任何数.a=0000,b=1011.a^b=1011,按位异或。

(2).a^a=0.

(3)a^b^a=b.

异或的一些例子:

(1)不引入第三个参数实现两个数的交换:

a=9=1001,b=11=1011.

a=a^b:1001^1011=0010

b=b^a;1011^0010=1001

a=a^b:0010^1001=1011

a=11,b=9.和a=a+b;b=a-b;a=a-b;有点像,位个人觉得就是用了异或特性的第三条。

(2)一个整数数组,除其中某一个元素只出现一次外,其他元素都出现了两次。求这个元素。

运用特性(3)代码如下:

int result=0;

for(int i=0;i<size;++i)

{

      result^=num[i];

}

cout<<result;

(3)接上面的那个题,一个整数数组中,除了两个元素只出现一次外,其他的都出现了两次。求这两个元素。

假设这两个元素是a和b,那么按照上面的处理result=a^b;假设a=9,b=11.那么result=0010,那么也就说a和b第三位不同,否则就会为0.那么我们现在把原数组的数按第三位分为两组:0组和1组。则a和b肯定不属于同一组。取某一组的数那么这组数一定是这样的:有a或者b,剩下的数都出现两次。让result的和这组数中所有的进行异或,便可以得到a或者b了。这个方法个人感觉只是提供了一种思路,并不一定是最好的,看题目有没有别的限制。

2.与操作

与操作的法则:1&0=0,1&1=1,0&0=0。全1和一个数求与还是那个数,全0和一个数求与就是置0.

3.或操作

或操作的法则:1|0=1,1|1=1,0|0=0.全0和一个数求或还是那个数,全1和一个数求或就是将那个数置为全1.

位操作的一些综合应用:

(1).用位操作求一个正整数除以7的余数。负整数我还没有试验过,先规定是正整数。

假设这个正整数是a,令x=a>>3,y=a&0x7,即a=(7+1)*x+y,如果x+y>7接着上面的做法做,直到得到的余数小于7为止。除以其他非2的次幂的思路都差不多。

(2).用位操作实现加法.

首先:异或操作相当于两个二进制相加但是没有进位的结果。与操作相当于求两个二进制数相加哪一位会有进位。

int add(int a,int b)
{
   int x=a&b;//进位
   int y=a^b;//未考虑进位得到的结果
   int t_x,t_y;//临时变量
   while(x)
   {
      t_x=x<<1;//进位的结果
      t_y=t;
      //将进位的结果加到y中
      x=t_x&t_y;
      y=t_x^t_y;
   }
}

(3)判断一个无符号的数是不是2的正整数次幂。

这个问题可以这么考虑:假设这个无符号的数为a,看a&(a-1)的结果,如果a是2的正整数次幂,那么结果应该是0,否则就不为0.

#include <iostream>
using namespace std;
int main()
{
   unsigned int a;
   cin>>a;
   int result=a&(a-1);
   if(result)
      cout<<"no"<<endl;
      else
	 cout<<"yes"<<endl;
}
这个问题可以进行一下拓展,就是判断一个无符号的正数(还是规定不为0吧,不然减掉1之后不知道表示什么了。)的二进制表示中有多少个1.代码如下:

#include <iostream>
using namespace std;
int main()
{
   unsigned int a;
   cin>>a;
   int count=0;
   while(a)
   {
      count++;
      a&=(a-1);
   }
   cout<<count<<std::endl;
   return 0;
}
实际每一次做&操作就去掉a中的一个1.
(4).求int的绝对值,int是32位的。

#include <iostream>
using namespace std;
int main()
{
   int x;
   cin>>x;
   int y=x>>31;
   cout<<y<<endl;
   cout<<(x^y)-y<<endl;
}
理解这段代码,需要好好理解计算机中补码移位的规则。

(5)将一个整数取反,如-8变成8,8变-8.8变-8在计算机中就是连同符号位在内所有位取反然后加1,所有位取反的方法就是和-1做异或。代码如下:

#include <iostream>
using namespace std;
int main()
{
   int a,result;
   cin>>a;
   result=(a^(-1))+1;
   cout<<result<<endl;
   return 0;
}

(6)高低位交换:给出一个16位的无符号整数。称这个二进制数的前八位为“高位“,后八位为”低位“。现在写一个程序把这个二进制的数的高低位交换。例如;10000110 11011000变成11011000 10000110设x=34520=10000110 11011000(二进制) 由于x为无符号数,右移时会执行逻辑右移即高位补0,因此x右移8位将得到0000000010000110。而x左移8位将得到11011000 00000000。可以发现只要将x>>8与x<<8这两个数相或就可以得到11011000 10000110。如果是有符号的数,那就要弄清楚补码的移位规则了。

(7)接上一个问题,二进制的逆续。第一步我们可以把这个二进制的数以两位为一组分成若干组,组内高低位交换。第二步以四位一组分成若干组,组内高低位交换,类推。代码如下:

#include <iostream>
using namespace std;
void display(int a)
{
   int stack[32]={0};
   int top=0;
   while(a>0)
   {
	  stack[top++]=a%2;
	  a/=2;
   }
   for(int i=31;i>=0;--i)
   {
	  cout<<stack[i];
   }
   cout<<endl;
}
int main()
{
   int a;
   cin>>a;
   cout<<"before reverse"<<endl;
   display(a);
   cout<<"after reverse"<<endl;
   a=((a>>1)&0x55555555)|((a<<1)&0xAAAAAAAA);
   a=((a>>2)&0x33333333)|((a<<2)&0xCCCCCCCC);
   a=((a>>4)&0x0F0F0F0F)|((a<<4)&0xF0F0F0F0);
   a=((a>>8)&0x00FF00FF)|((a<<8)&0xFF00FF00);
   display(a);
}
我这里输出二进制的时候是以32位做标准的,前面添0了。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:87077次
    • 积分:1079
    • 等级:
    • 排名:千里之外
    • 原创:22篇
    • 转载:7篇
    • 译文:0篇
    • 评论:28条
    文章分类
    最新评论