所谓Population Count算法,即是指计算一个二进制数中1的个数的算法。具体来说,就是任意给定一个无符号整数N,求N的二进制表示中1的个数,比如N = 5(0101)时,返回2;N = 15(1111)时,返回4。

这个问题是一个经典的面试题目,在实际中也有应用。关于这个问题,以下两篇博客文章中有较详细的论述:

详解二进制数中1的个数算法-求二进制数中1的个数

在此,仅对其中一些较为常规和较为巧妙的方法做一总结,并比较一下他们的执行效率。

直接法1

直接逐位判断,最直接也是最低效的方法。

char CountOne1(unsigned int num){  char N = 8 * sizeof(num);  char Count = 0;  while (N--)  {    Count += num & 1;    num >>= 1;  }  return Count;}

直接法2

在“直接法1”的基础上进行改进,当移位结果为0后就退出循环,这样循环次数与首位1的位置有关。最优情况下只循环一次,最坏情况下循环N次。

char CountOne2(unsigned int num){  char Count;  for (Count = 0; num != 0; num >>= 1)  {    Count += num & 1;  }  return Count;}

逐次清除最低位1

此方法利用num &= (num - 1)可清除最低位1的原理进行计数。num与num - 1的区别在于,num的最低位1在num - 1中变为了0,故二者取与后即可清除最低位1.

int CountOne3(unsigned int num){  char Count;  for (Count = 0; num != 0; Count++)  {      num &= (num - 1);  }  return Count;}

分支法

思路类似二分法,两两相加后即可得到结果,见下图:

在详解二进制数中1的个数中对此有详细描述。此方法复杂度为Log(N)(之前几种方法复杂度均为N),对于32位整数只需要计算5次即可。

char CountOne4(unsigned int num) {   num = (num & 0x55555555) + ((num >> 1)  & 0x55555555);   num = (num & 0x33333333) + ((num >> 2)  & 0x33333333);   num = (num & 0x0f0f0f0f) + ((num >> 4)  & 0x0f0f0f0f);   num = (num & 0x00ff00ff) + ((num >> 8)  & 0x00ff00ff);   num = (num & 0x0000ffff) + ((num >> 16) & 0x0000ffff);   return num ; }

三分法

这是一种很巧妙的方法,在此对其原理不多做说明,可参考之前提到的那两篇博客文章。

char CountOne5(unsigned int num){  unsigned int tmp = num - ((num >> 1) & 033333333333) - ((num >> 2) & 011111111111);  return ( (tmp + (tmp >> 3) ) & 030707070707) % 63;}

CPU指令实现

部分CPU中直接提供了指令来完成这一操作,这无疑是效率最高且最为简洁的方法。

在Intel x86中,如果CPU支持SSE4指令集,那可以使用POPCNT指令来计算二进制数中1的个数,实验表明,这应该是一个单周期指令,所以这无疑是最优的解决方案。

在MSVC下,可通过_mm_popcnt_u32()函数来调用POPCNT指令,详细信息可参考MSDN上的帮助。调用示例如下:

#include Count = _mm_popcnt_u32(num);

在GCC下,可直接调用__builtin_popcountll()函数,编译时加上编译选项-mpopcnt即可,此时编译器会自动使用POPCNT指令。

在ARM中,也有类似的指令,比如POPCOUNT宏(需要约10个时钟周期),NEON SIMD指令集中的VCNT及CNT指令。

POPCNT指令应该是有其实际重要作用的,比如在OpenCV的编译过程中就可以选择是否启用POPCNT指令。

执行时间对比

毫无疑问,直接调用CPU指令是最佳的解决方案,将其执行时间规定为1,对其他算法的执行时间进行归一化处理,结果如下:

从中可以看到,除了特别简单的情况下(如0x01),分枝法与三分法的执行效率是最好的,且这两种算法是稳定的算法,其执行时间不依赖于输入数据。逐次清除最低位1法在1的个数较少时也不失为一种好方法。

二进制拆弹实验详解_Population Count算法-求二进制数中1的个数相关推荐

  1. 二进制拆弹实验详解linux,拆解二进制炸弹

    拆解二进制炸弹 一.实验目的 1.理解C语言程序的机器级表示. 2.初步掌握GDB调试器的用法. 3.阅读C编译器生成的x86-64机器代码,理解不同控制结构生成的基本指令模式,过程的实现. 二. 实 ...

  2. 算法经典“钓鱼”问题详解 基于贪心算法 C语言描述

    算法经典"钓鱼"问题详解 基于贪心算法 初始条件 在一条水平路边,有 n 2 ≤ n ≤ 25个钓鱼池,从左到右编号为1.2.3.--.n.小明有H1 ≤ H ≤ 16个小时的空余 ...

  3. 【算法知识】详解希尔排序算法

    前言 已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 当待插入元素是一个很小(当需求是从小到大排序时,从大到小排序时此处为很大)直接插入排序需要移动 ...

  4. 【算法知识】详解直接插入排序算法

    前言 已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 在玩扑克牌的时候,我们抽到一张牌的时候,都是将它插入到当前手中牌的合适位置的. 如下图: (上图来自算法导论) 直接插入排序 ...

  5. 实验详解——Cobbler自动部署最小化安装

    实验详解--Cobbler自动部署最小化安装 一.实验:自动部署 二.Cobbler自动装机服务搭建步骤 1.导入epel源并加载在线安装源 2.安装Cobbler以及其相关服务软件包 3.修改cob ...

  6. 实验详解——DNS网关服务器的分离解析

    实验详解--DNS网关服务器的分离解析 一.实验图 二.要求 三.实验开始 1.网关服务器的配置 ①.新添加一块网卡,用双网卡来演示网关服务器的两个端口 ②.对两个网卡进行配置的修改 ③.重启网卡并查 ...

  7. 实验详解——DNS反向解析、DNS主服务器和从服务器的配置

    实验详解--DNS反向解析.DNS主服务器和从服务器的配置 实验一:DNS反向解析 1.安装bind 2.查找配置文件路径 3.配置/etc/named.conf主配置文件 4.修改/etc/name ...

  8. 详解FTP服务完成Linux和WIN10之间的信息传输(实验详解)

    详解FTP服务完成Linux和WIN10之间的信息传输(实验详解) 一.FTP简介 1. FTP服务--用来传输文件的协议 2.端口 3.数据连接模式 二.相关配置 1.安装FTP服务 2.设置匿名用 ...

  9. 详解 Linux环境中DHCP分配IP地址(实验详解)

    Linux中DHCP小实验详解 一.DHCP中继概述 二.DHCP在linux系统中的相关配置 1.配置DHCP服务器 2.设置全局配置参数 3.subnet网段声明 4.host主机声明 三.实验例 ...

最新文章

  1. Xcode消除编译器警告
  2. 玩转Android- 收藏集 - 掘金
  3. python numpy.savetxt(),np.column_stack保存(多行/列)数据 保存多维数组
  4. Pat乙级 1040 有几个PAT
  5. [R]R语言中的%%和%.%
  6. android studio zlib,在Android Studio 3.1中构建项目时出现“压缩执行失败”错误
  7. 天池 在线编程 最大得分(DP)
  8. 003.DNS主从正反解析部署
  9. cdoj31-饭卡(card) (01背包)
  10. 15.卷2(进程间通信)---门
  11. MySQL安装配置教程(超详细!)
  12. 2021年全球探针卡市场规模大约为158亿元(人民币),预计2028年将达到247亿元
  13. “极狐•华为HI版本”的尴尬与困境
  14. Phaser 使用介绍
  15. JAVA操作共享文件夹文件、下载、读取(windows、Linux通用)
  16. 盘点:视频监控行业的潜在商机
  17. React Native学习资源汇总
  18. python-半省略号、三个点、点点点、...符号的用法小结
  19. 使用restormer网络做2022年中兴捧月图像去噪
  20. 烽火HG680-KA/KB_Hi3798MV310_红外蓝牙语音_开启无线开关_通刷固件包

热门文章

  1. Linux内核分析-孟宁
  2. 1026. 节点与其祖先之间的最大差值
  3. python的idle支持两种方式_BO发布E8 Sport耳机,支持IP57防水
  4. html5游戏面试题,关于HTML5的十大面试题
  5. linux 复制文件到另一个目录命令_每天一条Linux命令(21) scp (远程文件复制)
  6. python3.7.2安装与pycharm_Python3和PyCharm安装与环境配置【图文教程】
  7. python中mean的用法_python 的numpy库中的mean()函数用法介绍
  8. linux 软件 tar deb rmp,deb、rpm、tar.gz三种Linux软件包的区别
  9. swarm部署mysql_「实战篇」开源项目docker化运维部署-借助dockerSwarm搭建集群部署(九)...
  10. Ubuntu, python, CUDA, cuDNN, 驱动, GCC ....的对应关系