Chinaunix首页 | 论坛 | 博客
  • 博客访问: 45666
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 176
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-31 01:57
个人简介

资深码农

文章分类

全部博文(15)

文章存档

2016年(3)

2014年(12)

我的朋友

分类: C/C++

2014-01-31 14:06:27

如何找到一个32位数中‘1’的个数呢?举个例子,给定一个输入,比如0x1013,那其中‘1’的个数便是4;再给定一个输入,比如是0xff00ff00,那其中‘1’的个数便是16。这个题目的解法有多种,第一反应是做一个循环,每次判断一下这个数的最低位(LSB)是否是1,然后向右移位,直到这个数为0为止。

点击(此处)折叠或打开

  1. unsigned long findOnes(unsigned long number)
  2. {
  3.     unsigned long count = 0;
  4.     for (; number; number = (number >> 1))
  5.         if (number & 0x1)
  6.             count++;
  7.     return count;
  8. }
测试程序如下,

点击(此处)折叠或打开

  1. int _tmain(int argc, _TCHAR* argv[])
  2. {
  3.     unsigned long testArray[] = {0, 0xffffffff, 0x12345678, 0x11111111, 0x22222222, 0x33333333};
  4.     for (size_t i = 0; i < sizeof(testArray)/sizeof(unsigned long); i++)
  5.     {
  6.         cout << "Input number is 0x" << hex << testArray[i] << ", totally " << dec << findOnes(testArray[i]) << " '1'(s) " << endl;
  7.     }

  8.     system("pause");
  9.     return 0;
  10. }
输出为
Input number is 0x0, totally 0 '1'(s)
Input number is 0xffffffff, totally 32 '1'(s)
Input number is 0x12345678, totally 13 '1'(s)
Input number is 0x11111111, totally 8 '1'(s)
Input number is 0x22222222, totally 8 '1'(s)
Input number is 0x33333333, totally 16 '1'(s)
程序看上去一切正常。可是是否还有更好的算法呢?是否能让计算变得更快呢?
我的一个改进的想法是用空间换时间,代码如下,

点击(此处)折叠或打开

  1. unsigned long fixedMap[] =
  2.     {
  3.         0, 1, 1, 2, 1, 2, 2, 3,
  4.         1, 2, 2, 3, 2, 3, 3, 4
  5.     };

  6. #define LOW_BIT4(NUM) ((NUM) & 0xF)

  7. unsigned long findOnes(unsigned long number)
  8. {
  9.     return fixedMap[LOW_BIT4(number)]        + fixedMap[LOW_BIT4(number>>4)]
  10.          + fixedMap[LOW_BIT4(number>>8)]     + fixedMap[LOW_BIT4(number>>12)]
  11.          + fixedMap[LOW_BIT4(number>>16)]    + fixedMap[LOW_BIT4(number>>20)]
  12.          + fixedMap[LOW_BIT4(number>>24)]    + fixedMap[LOW_BIT4(number>>28)];
  13. }

定义一个fixedMap,共16个元素,index代表数的大小,value代表这个数含有多少个‘1’,然后把32位长整形分割成8份,把每一份含有‘1’的个数做加法。


还有一个更现成一点的办法,那就是用STL提供的bitset来实现,

点击(此处)折叠或打开

  1. unsigned long findOnes(unsigned long number)
  2. {
  3.     bitset<32> tempBitSet((long)number);
  4.     return tempBitSet.count();
  5. }
此处使用long来强制转换number,因为bitset模板的构造函数并未提供输入参数是unsigned long类型的重载函数,所以要让编译器明确一下,否则会编译不过。这里稍微深入一下bitset,看一下内部的实现,我惊奇的发现,事实上bitset提供的count函数和我自己之前实现的空间换时间的版本的思路是基本一致的,如下

点击(此处)折叠或打开

  1. size_t count() const
  2.         {    // count number of set bits
  3.         static char _Bitsperhex[] = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4";
  4.         size_t _Val = 0;
  5.         for (int _Wpos = _Words; 0 <= _Wpos; --_Wpos)
  6.             for (_Ty _Wordval = _Array[_Wpos]; _Wordval != 0; _Wordval >>= 4)
  7.                 _Val += _Bitsperhex[_Wordval & 0xF];
  8.         return (_Val);
  9.         }
不过事实上使用bitset要更慢一些,原因是bitset的构造函数也要花费一定的时间,进行构造,代码清单如下

点击(此处)折叠或打开

  1. bitset(int _Ival)
  2.         {    // construct from bits in int
  3.         unsigned int _Val = (unsigned int)_Ival;
  4.         _Tidy();
  5.         for (size_t _Pos = 0; _Val != 0 && _Pos < _Bits; _Val >>= 1, ++_Pos)
  6.             if (_Val & 1)
  7.                 set(_Pos);
  8.         }

最后再列出一种网上某大哥写的方法,这个方法比较难理解,不过因为没有循环操作,效率亦很高,

点击(此处)折叠或打开

  1. unsigned long findOnes(unsigned long number)
  2. {
  3. #define C55 0x5555555555555555ULL
  4. #define C33 0x3333333333333333ULL
  5. #define C0F 0x0f0f0f0f0f0f0f0fULL
  6. #define C01 0x0101010101010101ULL

  7.     number -= (number >> 1) & C55;                      // put count of each 2 bits into those 2 bits
  8.     number = (number & C33) + ((number >> 2) & C33);    // put count of each 4 bits into those 4 bits
  9.     number = (number + (number >> 4)) & C0F; // put count of each 8 bits into those 8 bits
  10.     return (number * C01) >> 56;      // returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
  11. }






阅读(1001) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~