数的二进制表示中1的数目
- 我们知道, 每一个数都有唯一的二进制表示, 得到一个数的二进制表示, 对减小一些规模巨大的问题的计算复杂度具有重要的意义。那么在这里, 我们想要求得一个数的二进制表示当中1的数目。
- 首先, 一个最简单的思路就是, 我们求出每一位上的数字再加以统计, 就可以求出这个数的二进制表示当中1的数目, 其代码如下所示:
int__64 Compute(int__64 x)
{
int sum =0;
while(x >0){
sum += x%2;
x /=2;
}
return sum;
}
- 但是, 我们可以想到的是, 计算机在做取余和除法运算的时候, 所需要的代价往往较大, 所以我们就要使用更简单的运算来代替除法和取余运算。
- 我们知道, 在计算机中储存一个数, 是由这个数的二进制来储存的。所以我们可以使用移位和与的操作来判断最低位的数是1还是0,再加以判断,这样所花费的代价就大大减小了。
int__64 Compute(int__64 x)
{
int sum =0;
while(x >0){
sum += x&1;
x = x >> 1;
}
return sum;
}
- 那么, 还有没有节省时间的方法呢?
- 我们知道, 我们关心的只是二进制表示中1的数目, 与这中间的0毫无关联, 那么, 如何才能直接找到这个二进制当中的1呢?
- 我们便想到了减法, 由于二进制中的运算只有3种, 那么做一次减法就可以让最低位上的1借位, 再进行循环的话, 就可以解出二进制码中1的数目了。
int__64 Compute(int__64 x)
{
int sum =0;
while(x >0){
x &= (x-1);
sum++;
}
return sum;
}
这样,这个问题便很快解决了。
- 更多的, 我想到了去年ACM亚洲区比赛合肥站的一道题, 他要求出比一个数大而且数的二进制表示当中1的数目在指定范围的满足要求的最小的数。
- 我们使用上面的算法求出1的数目, 按照要求尽可能小的增大目标数。由上面的算法我们可以知道, 减法我们可以增加1的数目, 那么加法就是逆向的操作可以减小1的数目。而若要增大这个数且增加1的数目, 就可以使用或的操作使最低位的0变为1, 具体的代码如下所示:
-
#include<iostream> #include<cstdio> #include<algorithm> #define LL long long using namespace std; LL start; int s1, s2; int t; int Compute(LL st){ int su =0; while(st){ st =st & (st-1); su++; } return su; } int main() { int i; scanf("%d",&t); for(i =1;i <=t;i++){ scanf("%lld %d %d",&start,&s1,&s2); start+=1; while(1){ int temp =Compute(start); if(temp>=s1 && temp<=s2)break; else if(temp<s1){ LL temp2 =start+1; start =(start | temp2); } else start++; } printf("Case #%d: %lld\n",i,start); } }