前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >位运算详解

位运算详解

作者头像
他叫自己MR.张
发布2019-07-01 17:06:21
5090
发布2019-07-01 17:06:21
举报
文章被收录于专栏:Android必知必会Android必知必会

(1)、按位与(&),将两个操作数化为二进制后并将对应的每一位分别进行逻辑与操作。(a%(2^n)=a&(2^n-1)) (2)、按位或(|),将两个操作数化为二进制后并将对应的每一位分别进行逻辑或操作。 (3)、按位异或(^),和以上同,异或是指对应位相同则运算结果为0,否则为1。 (4)、按位取反(~),对每一位进行取反。(求x的相反数:x=(~x+1)) (5)、移位。分为左移(<<)和右移(>>),左移是按照指定的位数将一个数的二进制值向左移位,左移后,低位补0,移除的高位舍去,右移相反。 等价转换:a<<n=a*(2^n),a>>n=a/(2^n)。

代码语言:javascript
复制
#include<iostream>
using namespace std;
int n=7,m=9;
int main()
{ cout<<(n&m)<<endl; 
    /******************** 
    7: 0 0 0 0 0 1 1 1
    9:(&) 0 0 0 0 1 0 0 1
    _____________________
          0 0 0 0 0 0 0 1 
    所以输出结果为1。 
    ********************/ 
    cout<<(n|m)<<endl; //输出(1111)2=15 
    cout<<(n&1)<<endl; //判断一个数是否为奇数,n&1=1则为奇数,否则为偶数
    cout<<(~n)<<endl; //(11111000)2=-8,前面的的1为符号位
    cout<<(n^m)<<endl; //(1110)2=14
    cout<<(n<<2)<<endl; //(11100)2=28
    cout<<(m>>2)<<endl; //(10)2=2 
    n=n^m,m=m^n,n=n^m; //可以用来交换两个数值
    /**********************************
    n=n^m,所以m=m^n=m^(n^m)=(m^m)^n=7
    n=n^m=(n^m)^(m^m)^n=(n^n)^(m^m)^m=9
    **********************************/ 
    cout<<n<<" "<<m<<endl;
    return 0; 
}

位运算的应用: 求平均值:求(x+y)/2时,可能x+y会超过int的最大值,可以用位运算来求: int Ave(int x,int y) { return x&y+((x^y)>>1); } 判断一个数是否能够写成2的幂次方形式。bool comp(int x) { if((x&(x-1))==0) return true; return false; } 统计一个数的二进制数中1的个数。利用x=x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1会将该位变为0.int Count(int x) { int sum=0; while(x) { sum++; x=x&(x-1); } return sum; } 例题1:NYOJ 222(整数中的1),求区间[a,b]内所有整数的二进制的1的个数。位运算的强大应用~~~使用上面的代码的话肯定超时~~a,b的范围太大了~~呵呵,还是贴下,有些肯定用的上~~

代码语言:javascript
复制
#include<iostream>
#include<cstdio> 
using namespace std;
int Count(int x)
{ int sum=0;
    while(x)
    { sum++;
        x=x&(x-1);
    }
    return sum;
} 
int main()
{ int a,b;
    while(scanf("%d%d",&a,&b)!=EOF)
    { int sum=0;
        for(int i=a;i<=b;i++)
            sum+=Count(i);
        printf("%d\n",sum); 
    } 
    return 0;
} 

方法2:打表法,都知道是以空间换时间,直接调用效率肯定是最高的~首先我们可以保存从0~2^32中每个数的二进制1的个数,然后直接调用即可,但是这样打表似乎太困难了点,这个时候我们可以值保存0~255中的每个数的二进制值中1的个数,然后进行分段查询即可。至于这个打0~255的表的话可以利用上面的代码打出~打表的程序:

代码语言:javascript
复制
#include<iostream>
using namespace std;
int Count(int x)
{ int sum=0;
    while(x)
    { sum++;
        x=x&(x-1);
    }
    return sum;
} 
int main()
{ for(int i=0;i<256;i++)
        cout<<Count(i)<<",";
    cout<<endl; 
    return 0;
} 

然后直接全选复制保存到一个数组中即可。将32位整形分为四段,求出每一段的1的个数和相加即为该数二进制值的1的个数,问题是如何求出每一段中的1的个数,比方说我要求后面k位数的1的个数,可以这样做://x * * * * * * * * * * //y(&) ....1 1 1 1 1(y=2^k-1),有k个1 //_______________________ //不管x的最后k位是什么,总之只有1&1=1,所以后面k位数与n&(2^k-1)这个数化为2进制的结果相同 下面的代码只是分为了四段,那么数组大小定义为256=2^8即可,这个时候每次应该移走8位了~当然你还可以取其它的数,关于位运算的一些公式: (1)、取末尾k位:x&(2^k-1); (2)、在最后一位加一个1:x<<1+1; (3)、把最后一位变为1:x|1;把最后一位变为0:x|1-1; (4)、把右数第k位变为1:x|(1<<(k-1)),变为0:x&~(1<<(k-1)); (5)、把末尾k位变为1:x|(1<<k-1),变为0:x&~(1<<k-1); (5)、去掉右起第一个1的左边|,如(100101000->1000):x & (x ^ (x-1)) 。等等~~ 这个时候的代码就简单了~~

代码语言:javascript
复制
#include<iostream>
using namespace std;
int a[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
           };
int Count(int n)//计算n的二进制值中1的个数 
{ int sum=0;
    for(;n>0;n>>=8)
        sum+=a[n&255];
    return sum; 
} 
/**********************************************
int Count(int n)
{ unsigned char *ptr=(unsigned char *)&n; 
    return a[ptr[0]+a[ptr[1]+a[ptr[2]+a[ptr[3]; 
}
**********************************************/ 
int main()
{ int a,b;
    cin>>a>>b;
    int sum=0;
    for(int i=a;i<=b;i++)
        sum+=Count(i);
    cout<<sum<<endl; 
    return 0;
} 

使用这个方法的话真的是险过,但是你可以把数组做适当的调整,调大点,增加空间来降低时间,恩~~这个数组要能够写成2的幂次方的形式~ 方法3:并行计算,利用二分法的思想求解~~难得想到,呵呵~我是没有想到,先贴出代码:暂时不懂,只先贴下代码,

代码语言:javascript
复制
#include<iostream>
using namespace std;
int Count(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;
} 
int main()
{ int a,b,sum=0;
    cin>>a>>b; 
    for(int i=a;i<=b;i++)
        sum+=Count(i);
    cout<<sum<<endl; 
    return 0;
} 

方法4:来源于这道题目后面的讨论,这种做法就更牛了,还是看不懂,贴下~~直接刷到0秒!!!NYOJ 281(整数中的1-2)可以用这个方法过了

代码语言:javascript
复制
#include<iostream>
using namespace std;
int Count(int n,int m)
{ if((n>>m)==0) return 0;
    int d=(n>>m)&1;
    if(d==1) return (n>>(m+1))*(1<<m)+(n&((1<< m)-1))+1+Count(n,m+1);
    else return (n>>(m+1))*(1<<m)+Count(n,m+1);
}
int main()
{ int a,b;
    cin>>a>>b;
    int n=(a==0?1:a);
    cout<<Count(b,0)-Count(n-1,0)<<endl; 
    return 0;
} 

例题2:NYOJ 528(找球号),充分利用^的作用:x^x=0,0^x=x。具体见代码:

代码语言:javascript
复制
#include<iostream>
#include<cstdio> 
using namespace std;
int num,value;
int main()
{ while(scanf("%d",&num)!=EOF)
    { int sum=0;
        for(int i=0;i<num;i++)
        { scanf("%d",&value);
            sum^=value;
        } 
        printf("%d\n",sum);
    }
    return 0; 
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2014年05月07日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档