经典问题7 c/c++ 程序设计 ---0、1串个数统计问题
2010-09-28 09:02
357 查看
(1)面试题:下面程序的结果是多少?
1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 int main()
7 {
8 int count = 0;
9 int m=9999;
10 while(m){
11 count++;
12 m=m&(m-1);
13 }
14 cout<<"the count of the number 1 is:"<<count<<endl;
15 return 0;
16 }
------------------
$ ./a.out
the count of the number 1 is:8
------------------
知识点:统计二进制数中1的个数;
拓展:问题:给出一个整数,请设计算法计算该整数以二进制格式表示时的1的个数。
例如,十进制整数150,二进制表示为10010110,则1的个数为4个。要求算法效率尽可能的高。
解答:
方法1:分别判断各个位
--------------------------------------
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n >>= 1)
{
count += n & 1;
}
return count;
}
----------------------------------------
方法2:循环中直接计算1的数量
如何只数'1'的个数?
1)如果一个数字至少包含一个'1'位,那么这个数字减1将从最低位开始依次向高位借位,
直到遇到第一个不为'0'的位。依次借位使得经过的位由原来的'0'变为'1',而第一个遇到的那个'1'位则被借位变为'0'。
36 d = 100100 b
36-1 d = 100011 b
2)如果最低位本来就是'1',那么没有发生借位。
现在把这2个数字做按位与:n & n-1的结果是什么?
2个数字在原先最低为'1'的位以下(包括这个位)的部分都不同,所以结果是保留了其他的'1'位。
36 & 36-1 d = 100000 b
这个结果刚好去掉了最低的一个'1'位
----------------------------------
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n &= n - 1)
{
count++;
}
return count;
}
-------------------------------------
方法3:并行计算的
------------------------------------------------------------
#define POW(c) (1<<(c))
#define MASK(c) (((unsigned long)-1) / (POW(POW(c)) + 1))
#define ROUND(n, c) (((n) & MASK(c)) + ((n) >> POW(c) & MASK(c)))
int bit_count(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;
}
---------------------------------------------------------------
一下子看不明白,先把宏展开来:
POW是计算2的幂
MASK很奇怪,一个全1的无符号数字除以2的幂的幂加1?
好在打印出来还能看得懂:
------------------------------------------------------------
MASK(0) = 55555555 h = 01010101010101010101010101010101 b
MASK(1) = 33333333 h = 00110011001100110011001100110011 b
MASK(2) = 0f0f0f0f h = 00001111000011110000111100001111 b
MASK(3) = 00ff00ff h = 00000000111111110000000011111111 b
MASK(4) = 0000ffff h = 00000000000000001111111111111111 b
--------------------------------------------------------------
这些mask分别把32位数字划分为几个部分。每个部分的前一半和后一半分别是全'0'和全'1'。
MASK(0)分为16个部分,MASK(1)分为8个部分,...
ROUND中对n的处理:(n & MASK) + (n >> POW & MASK)
POW的值刚好是MASK中连续'0'(或者连续'1')的长度。也就是说ROUND把由MASK分开的n的各个部分中的高POW位和低POW位相加。
为了便于说明,取一个简单的部分:MASK(1)的0011
假设n的值为1001,那么ROUND后的结果就是10 + 01 = 11 b,把这个结果赋值给n,这时n的含义由原来的二进制位串变为'1'位的数量。
特别的,当ROUND(n, 0)时,把n当作一个32个部分各自'1'位的数量。('0'表示没有'1',而'1'则表示有1个'1')
计算完n = ROUND(n, 0)后,n是一个16个部分各自'1'位数量的'数组',这个'数组'的每个元素只有2个二进制位。最大值为2,足够由2个二进制位来表示。
接下来,计算完n=ROUND(n,1)后,n是一个8个部分各自'1'位数量的'数组',这个'数组'的每个元素只有4个二进制位。
最大值为4,足够由4个二进制位来表示。(实际只需要3个二进制位)
...
最后一步,计算n=ROUND(n,4)后,n是一个1个部分各自'1'位数量的'数组',这个'数组'的每个元素有32个二进制位。
最大值为32,足够由32个二进制位来表示。(实际只需要6个二进制位)
这个代表32位内'1'位数量的32位二进制数也就是我们要求的结果。
这个方法的好处是只需要5步(log n (n=32)),并且没有循环(或分支),流水线不会被打破。
下面举例说明:
from:http://en.wikipedia.org/wiki/Hamming_weight
1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 int main()
7 {
8 int count = 0;
9 int m=9999;
10 while(m){
11 count++;
12 m=m&(m-1);
13 }
14 cout<<"the count of the number 1 is:"<<count<<endl;
15 return 0;
16 }
------------------
$ ./a.out
the count of the number 1 is:8
------------------
知识点:统计二进制数中1的个数;
拓展:问题:给出一个整数,请设计算法计算该整数以二进制格式表示时的1的个数。
例如,十进制整数150,二进制表示为10010110,则1的个数为4个。要求算法效率尽可能的高。
解答:
方法1:分别判断各个位
--------------------------------------
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n >>= 1)
{
count += n & 1;
}
return count;
}
----------------------------------------
方法2:循环中直接计算1的数量
如何只数'1'的个数?
1)如果一个数字至少包含一个'1'位,那么这个数字减1将从最低位开始依次向高位借位,
直到遇到第一个不为'0'的位。依次借位使得经过的位由原来的'0'变为'1',而第一个遇到的那个'1'位则被借位变为'0'。
36 d = 100100 b
36-1 d = 100011 b
2)如果最低位本来就是'1',那么没有发生借位。
现在把这2个数字做按位与:n & n-1的结果是什么?
2个数字在原先最低为'1'的位以下(包括这个位)的部分都不同,所以结果是保留了其他的'1'位。
36 & 36-1 d = 100000 b
这个结果刚好去掉了最低的一个'1'位
----------------------------------
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n &= n - 1)
{
count++;
}
return count;
}
-------------------------------------
方法3:并行计算的
------------------------------------------------------------
#define POW(c) (1<<(c))
#define MASK(c) (((unsigned long)-1) / (POW(POW(c)) + 1))
#define ROUND(n, c) (((n) & MASK(c)) + ((n) >> POW(c) & MASK(c)))
int bit_count(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;
}
---------------------------------------------------------------
一下子看不明白,先把宏展开来:
POW是计算2的幂
MASK很奇怪,一个全1的无符号数字除以2的幂的幂加1?
好在打印出来还能看得懂:
------------------------------------------------------------
MASK(0) = 55555555 h = 01010101010101010101010101010101 b
MASK(1) = 33333333 h = 00110011001100110011001100110011 b
MASK(2) = 0f0f0f0f h = 00001111000011110000111100001111 b
MASK(3) = 00ff00ff h = 00000000111111110000000011111111 b
MASK(4) = 0000ffff h = 00000000000000001111111111111111 b
--------------------------------------------------------------
这些mask分别把32位数字划分为几个部分。每个部分的前一半和后一半分别是全'0'和全'1'。
MASK(0)分为16个部分,MASK(1)分为8个部分,...
ROUND中对n的处理:(n & MASK) + (n >> POW & MASK)
POW的值刚好是MASK中连续'0'(或者连续'1')的长度。也就是说ROUND把由MASK分开的n的各个部分中的高POW位和低POW位相加。
为了便于说明,取一个简单的部分:MASK(1)的0011
假设n的值为1001,那么ROUND后的结果就是10 + 01 = 11 b,把这个结果赋值给n,这时n的含义由原来的二进制位串变为'1'位的数量。
特别的,当ROUND(n, 0)时,把n当作一个32个部分各自'1'位的数量。('0'表示没有'1',而'1'则表示有1个'1')
计算完n = ROUND(n, 0)后,n是一个16个部分各自'1'位数量的'数组',这个'数组'的每个元素只有2个二进制位。最大值为2,足够由2个二进制位来表示。
接下来,计算完n=ROUND(n,1)后,n是一个8个部分各自'1'位数量的'数组',这个'数组'的每个元素只有4个二进制位。
最大值为4,足够由4个二进制位来表示。(实际只需要3个二进制位)
...
最后一步,计算n=ROUND(n,4)后,n是一个1个部分各自'1'位数量的'数组',这个'数组'的每个元素有32个二进制位。
最大值为32,足够由32个二进制位来表示。(实际只需要6个二进制位)
这个代表32位内'1'位数量的32位二进制数也就是我们要求的结果。
这个方法的好处是只需要5步(log n (n=32)),并且没有循环(或分支),流水线不会被打破。
下面举例说明:
from:http://en.wikipedia.org/wiki/Hamming_weight
相关文章推荐
- 经典问题7:c/c++ 程序设计 ---0、1串个数统计问题
- 经典问题4:c/c++ 程序设计 ---float类型转换问题
- 经典问题9:c/c++ 程序设计 ---基本数据处理问题
- 经典问题3:c/c++ 程序设计 ---类型转换问题
- 经典问题5:c/c++ 程序设计 ---usigned char类型转换问题
- 经典问题2:c/c++ 程序设计 ---i++问题
- 经典问题6:c/c++ 程序设计 ---与或非问题
- 经典问题1 :c/c++ 程序设计 ---赋值语句
- 经典问题8:c/c++ 程序设计 ---bit位逆转高效算法问题
- 经典问题8:c/c++ 程序设计 ---bit位逆转高效算法问题
- 经典问题9:c/c++ 程序设计 ---基本数据处理问题
- 经典问题:c/c++ 程序设计 ---赋值语句
- 经典问题2:c/c++ 程序设计 ---i++问题
- 经典问题6 c/c++ 程序设计 ---与或非问题
- 经典问题3:c/c++ 程序设计 ---类型转换问题
- 经典问题5:c/c++ 程序设计 ---usigned char类型转换问题
- 经典问题4:c/c++ 程序设计 ---float类型转换问题
- Problem J: C/C++经典程序训练5---图形打印问题
- C/C++语言经典、实用、趣味程序设计编程百例精解(8)
- 【C++深度剖析教程14】经典问题解析三之关于赋值的疑问