【题目描述】
统计十进制数的二进制表示中1的个数。
【算法分析】
虽然曾在 https://blog.csdn.net/hnjzsyjyj/article/details/110148954 发过一篇关于“统计十进制数的二进制表示中1的个数”的博客,但本文实现了一种新的思路。此思路来源于清华大学邓俊辉版《数据结构》http://dsa.cs.tsinghua.edu.cn/~deng/ds/src_link/countones/countones_2.cpp.htm 中的内容。现分析如下:
● POW(x) 函数
自定义的 POW(x) 函数,运行后得到 2^x。
int POW(int x){
return 1<<x; //2^x
}
● MASK(x) 函数
自定义的 MASK(x) 函数,运行后得到以 2^x 个 0 及 2^x 个 1 相间的32位的二进制数。这是本文算法的一个重要核心点。
int MASK(int x){
int t=POW(x);
return ((unsigned long) -1)/(POW(t)+1);
}
其中,MASK(0)、MASK(1)、MASK(2)、MASK(3)、MASK(4)的值如下所示。
MASK(0) = 01010101010101010101010101010101(b) = 55555555(h)
MASK(1) = 00110011001100110011001100110011(b) = 33333333(h)
MASK(2) = 00001111000011110000111100001111(b) = 0f0f0f0f(h)
MASK(3) = 00000000111111110000000011111111(b) = 00ff00ff(h)
MASK(4) = 00000000000000001111111111111111(b) = 0000ffff(h)
● ROUND(x,c) 函数
自定义的 ROUND(x,c) 函数,运行时以 2^c 位为单位进行分组,相邻的组两两捉对累加给定的十进制数对应的二进制数中 1 的个数。这个函数是导致本文算法时间复杂度低的重要原因。
int ROUND(int x,int c){
int u=x&MASK(c);
int v=(x>>POW(c))&MASK(c);
return u+v;
}
● 系统自带的求以 2 为底的 x 的对数 log2(x)
#include <bits/stdc++.h>
using namespace std;
int main() {
int x;
cin>>x;
cout<<log2(x);
return 0;
}
/*
in:8
out:3
*/
【算法代码】
#include <bits/stdc++.h>
using namespace std;
int POW(int x){
return 1<<x; //2^x
}
int MASK(int x){ //得到以2^x个0及2^x个1相间的32位的二进制数
int t=POW(x);
return ((unsigned long) -1)/(POW(t)+1);
}
int ROUND(int x,int c){
int u=x&MASK(c);
int v=(x>>POW(c))&MASK(c);
return u+v;
}
int countOnes(unsigned int n){
n=ROUND(n,0);
n=ROUND(n,1);
n=ROUND(n,2);
n=ROUND(n,3);
n=ROUND(n,4);
return n;
}
int main() {
int x;
cin>>x;
cout<<countOnes(x);
return 0;
}
/*
in:123
out:6
*/
【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/110148954
https://dsa.cs.tsinghua.edu.cn/~deng/ds/src_link/countones/countones_2.cpp.htm
https://vimsky.com/examples/usage/cpp-programming_library-function_cmath_log2.html