求二进制中1的个数

求二进制数中1的个数(编程之美) 求二进制数中1的个数继京东618店庆时买的《编程之美》这本书,翻了翻,发现里面的题还是挺有意思的,看起来我们觉得很简单的题目,解法却有很多很多种,真是一个比一个巧妙,于是,决定记录一下。书中的题目如下 对于一个字节(8bit)的无符号数,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。 阅读详情

 

 

求二进制数中 1 的个数

对于一个字节(8bit)的变量,求其二进制表示中“1”的个数,要求算法的执行效

率尽可能地高。

 

 

分析与解法

大多数的读者都会有这样的反应:这个题目也太简单了吧,解法似乎也相当地单一,不

会有太多的曲折分析或者峰回路转之处。 那么面试者到底能用这个题目考察我们什么呢?事

实上,在编写程序的过程中,根据实际应用的不同,对存储空间或效率的要求也不一样。比

如在 PC 上的程序编写与在嵌入式设备上的程序编写就有很大的差别。我们可以仔细思索一

下如何才能使效率尽可能地“高”。

【解法一】

可以举一个八位的二进制例子来进行分析。对于二进制操作,我们知道,除以一个 2,

原来的数字将会减少一个 0。如果除的过程中有余,那么就表示当前位置有一个 1。

以 10 100 010 为例;

第一次除以 2 时,商为1 010 001,余为 0。

第二次除以 2 时,商为101 000,余为 1。

因此,可以考虑利用整型数据除法的特点,通过相除和判断余数的值来进行分析。于是

有了如下的代码。

代码清单 2-1

int Count(int v)

{

int num = 0;

while(v)

 {

  if(v % 2 == 1)

 {

  num++;

 }

  v = v/ 2;

 }

return num;

}

【解法二】使用位操作

前面的代码看起来比较复杂。我们知道,向右移位操作同样也可以达到相除的目的。唯

一不同之处在于,移位之后如何来判断是否有 1 存在。对于这个问题,再来看看一个八位的

数字:10 100 001。

在向右移位的过程中,我们会把最后一位直接丢弃。因此,需要判断最后一位是否为1,

而“与”操作可以达到目的。可以把这个八位的数字与00000001进行“与”操作。如果结果为1,

则表示当前八位数的最后一位为1,否则为0。代码如下:

代码清单 2-2

 

int Count(int v)

{

 int num = 0;

 While(v)

 {

  num += v &0x01;

  v >>= 1;

 }

 return num;

}

【解法三】

位操作比除、余操作的效率高了很多。但是,即使采用位操作,时间复杂度仍为 O

(log2v),log2v 为二进制数的位数。那么,还能不能再降低一些复杂度呢?如果有办法让

算法的复杂度只与“1”的个数有关,复杂度不就能进一步降低了吗?

同样用 10 100 001 来举例。如果只考虑和 1 的个数相关,那么,我们是否能够在每次判

断中,仅与 1 来进行判断呢?

为了简化这个问题,我们考虑只有一个 1 的情况。例如:01 000 000。

如何判断给定的二进制数里面有且仅有一个1呢?可以通过判断这个数是否是2的整数

次幂来实现。另外,如果只和这一个“1”进行判断,如何设计操作呢?我们知道的是,如果

进行这个操作,结果为 0 或为1,就可以得到结论。

如果希望操作后的结果为 0,01 000 000 可以和00 111 111 进行“与”操作。

这样,要进行的操作就是 01 000 000 &(01 000 000 – 00 000 001)= 01 000 000 &

00 111 111 = 0。

因此就有了解法三的代码: 

代码清单 2-3

int Count(int v)

{

 int num = 0;

 while(v)

 {

  v &= (v-1);

  num++;

 }

 return num;

}

【解法四】使用分支操作

解法三的复杂度降低到 O(M),其中 M 是v 中1 的个数,可能会有人已经很满足了,

只用计算 1的位数,这样应该够快了吧。然而我们说既然只有八位数据,索性直接把 0~255

的情况都罗列出来,并使用分支操作,可以得到答案,代码如下:

 

代码清单 2-4

int Count(int v)

{

 int num = 0;

 switch (v)

 {

  case 0x0:

   num = 0;

   break;

  case 0x1:

  case 0x2:

  case 0x4:

  case 0x8:

  case 0x10:

  case 0x20:

  case 0x40:

  case 0x80:

   num = 1;

   break;

  case 0x3:

  case 0x6:

  case 0xc:

  case 0x18:

  case 0x30:

  case 0x60:

  case 0xc0:

   num = 2;

   break;

   //...

 }

 return num;

}

解法四看似很直接,但实际执行效率可能会低于解法二和解法三,因为分支语句的执行

情况要看具体字节的值,如果 a =0,那自然在第 1 个case就得出了答案,但是如果 a =255,

则要在最后一个 case 才得出答案,即在进行了 255 次比较操作之后!

看来,解法四不可取!但是解法四提供了一个思路,就是采用空间换时间的方法,罗列

并直接给出值。如果需要快速地得到结果,可以利用空间或利用已知结论。这就好比已经知

道计算 1+2+ … +N 的公式,在程序实现中就可以利用公式得到结论。

最后,得到解法五:算法中不需要进行任何的比较便可直接返回答案,这个解法在时间

复杂度上应该能够让人高山仰止了。

【解法五】查表法

代码清单 2-5

/* 预定义的结果表 */

int countTable[256] =

{

 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,

    3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3,

    4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,

    3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3,

    4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6,

 

    6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,

    5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,

    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3,

    4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4,

    4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6,

    7, 6, 7, 7, 8

};

int Count(int v)

{

 //check parameter

 return countTable[v];

}

这是个典型的空间换时间的算法,把 0~255 中“1”的个数直接存储在数组中,v 作为数组

的下标,countTable[v]就是 v 中“1”的个数。算法的时间复杂度仅为 O(1)。

在一个需要频繁使用这个算法的应用中,通过“空间换时间”来获取高的时间效率是一个

常用的方法,具体的算法还应针对不同应用进行优化。

扩展问题

1. 如果变量是32位的DWORD,你会使用上述的哪一个算法,或者改进哪一个算法?

2. 另一个相关的问题,给定两个正整数(二进制形式表示)A和B,问把A变为B需要改变

多少位(bit)?也就是说,整数A 和B 的二进制表示中有多少位是不同的?

 

 

快速求一个32位无符号整数二进制中为1的个数 一个数5,二进制表示法,为101,则有2个1,如何快速求出一个32位无符号整数中1的个数,如果要我们来做,我们可能会进行一个32次的循环,从1至32,依次测试相应的位;这个算法不是最优的。以下算法效率的要快很多:http://aggregate.ee.engr.uky.edu/MAGIC/。源代码如下 unsigned int ones32(register unsigned int x) { /* 32-bit recursive reduction using SWAR... 阅读详情

相关推荐

android+音乐节拍检测,科学网—音乐节拍跟踪或音乐节拍检测软件,LilyBeats alpha - 石自强的博文...

我们设计并实现了一种用于实时跟踪音乐节拍的软件。该软件可以用于智能灯方案提供商,为其提供灯光随音乐变化的核心解决方案。首先给出软件的下载链接:或者音乐节拍是乐曲中表示固定单位时值和强弱规律的组织形式,亦称拍子。它有两个特性:周期性和连续性。节拍周期性表现为节拍结构,是乐曲中周期性出现的节奏序列。节拍连续性表现为音乐的平均速度,其单位为bpm(beat per minute每分钟音乐节拍的个数)。音...

weixin_39571403的博客 1400

【C语言】求二进制中1的个数

求二进制中1的个数

记录学习 834

STM32F407通过FSMC接口与LAN9252的EtherCAT从站移植实战

本文详细介绍了在STM32F407平台上,通过FSMC接口驱动LAN9252 EtherCAT从站控制器的完整移植实战。内容涵盖硬件连接、FSMC时序配置、官方SDK代码分析与关键修改、EtherCAT协议栈集成适配,以及实战调试与稳定性优化技巧,旨在帮助工程师解决工业通信中的核心挑战,实现稳定高效的实时控制网络接入。

weixin_29268267的博客 274

如何求二进制中1的个数?

这道题刚开始开起来比较懵逼的感觉,之前没有见过,经过Y总的讲解和自己线下的查找,怎会这个题目需要提前具备二进制的基本运算,与、或、非、同或、异或、与非等运算,前三个为基本的必备的技能,后三个可以也学习一下。在做lowbit的操作的时候,进行设置一个标志位,因为每次都是压出从右到左的第一个1,及1之后存在的数字,每次都会压出一个1,压出一次,我们的而标志位加1,最后输出标志位的值就行。异或:只有两个数字是不同(eg: 0:1 \ 1:0 )的才为1,否则都为0;或:只有两个为0,最后结果为1,其余全为0;

2301_78460317的博客 555

【5G核心网】Packet Forwarding Model 数据包转发模型

CP 通过建立,修改,删除 PFCP 会话上下文,以及通过为每个 PFCP 会话赏析哪问提供 PDR FAR QER URR BAR,来控制 UP 面的数据包处理。 每一个 PDR 都包含 PDI(Packet Detection Information,包探测信息),一个或多个字段,与输入数据包进行匹配,并可能与以下规则相关联,从而提供了适用于与 PDI 匹配的数据包的指令集: - One FAR(包含了处理数据包的相关指令) an Apply Act...

zhonglinzhang 1万+

关于Vscode配置Unity环境时的一些报错问题(持续更新)

3.找到设置文件,添加如下代码,用于告诉VSCode)在处理 C# 项目时优先使用 C# 扩展来加载和管理项目,而不是使用 C# Dev Kit 或其他扩展。1.很可能是unity的插件版本问题,打开window > package manager将。2.很可能是项目文件的问题,建议unity打开edit > perfercenes。3.手动重装其他版本的.Net框架(去官网找,多下几个无所谓的)解决:(由于造成问题不一样,所以建议都尝试一次)下载net请求超时(一般都会超时很正常的)

bug制造机 1887

【C语言】求二进制中1的个数(三种方法)

该方法也是与其他两种方法拉开水平的一种,如果你想让别人感觉你对二进制位理解的很到位,你就可以写这种方法。=的优先级是高于&的所以要加(),那也就是说我们直接写成。因为2的二次幂的二进制中只有一个1,所以,只二进制中只有1个1的时候才能说明是2的2次幂。我个人还是比较推荐移动所求数的二进制位,因为得出来的结果只可能是1或0;那么通过不断的左移,如果左移后&的结果不为0就说明有一个1,如果说我们能让1左移1位就能得到二进制的第二位是多少,然后其实我们也可以直接移动i的二进制位,不断的右移。

HeXIAOxia_的博客 1809

程序进入hardfault之后查找原因的方法

如果在MCU运行程序的时候,进入了hardfault,该怎么办?

smart_boy__的博客 761

【C语言】三种方法求二进制中1的个数

输出一个整数的32位二进制中1的个数

weixin_67711401的博客 4240

AI大模型助力现金流精准预测案例详解

因为它的“注意力机制”能像“人一样思考”——比如,当预测“下周现金流”时,模型会重点关注“最近7天的销售数据”“未来7天的天气”“竞品是否有促销”这些关键因素,而忽略“去年同期的无关数据”。AI大模型不是“取代”财务人员,而是“赋能”他们——把财务人员从“重复的Excel操作”中解放出来,专注于“战略决策”(比如如何优化资金结构、如何提升资金利用率)。比如,“促销力度”这个特征,不是简单的“折扣率”,而是“折扣率×促销天数×覆盖SKU数量”,这样能更准确反映促销对现金流的影响。这些问题的核心,其实是。

AIGC应用创新大全的博客 1258

YOLOv13改进策略【Backbone/轻量化】| 替换骨干网络 CVPR-2024 StarNet,超级精简高效的轻量化模块

本文记录的是。StarNet设计简洁,没有复杂的设计和精细调整的超参数,仅是一个 4 阶段的分层架构。并且其中能够在低维空间计算的同时考虑极高维的特征这一特性,进而提高模型精度。本文在替换骨干网络中配置了原论文中的starnet_s1starnet_s2starnet_s3和starnet_s4七种模型,以满足不同的需求。

Limiiiing的博客 669

求一个整数的二进制序列中1的个数

11>>1的补码00000000000000000000000000000101。可知n = n & (n - 1)每执行一次n的二进制序列中1的个数就减少1个。当n=0时退出循环,此时count = 3 也就是二进制中1的个数。可知右移后的二进制序列的最低位只要是1,按位与操作后的结果就是1,也就是说n的二进制序列中有几个1循环就执行几次,大大提高代码效率。这里假设 n = 11 , i = 1。经过n = n & (n - 1)操作后。经过n = n & (n - 1)操作后。

2402_89035880的博客 933

车载以太网:PHY(物理层)介绍

本文以TJA1101芯片为实例介绍了车载以太网PHY(物理层)组成。

kevin1499的博客 6231

求二进制数中1的个数

对于一个字节(8bit)的变量,求其二进制表示中”1”的个数,要求算法的执行效率尽可能的高。 这道题看似简单,但对于这道题作者却给了出五中解法。 解法一:每次求余,看余数是否为1,如果是的话就累计加一,然后再除以2,重复以上步骤,直到为0,最后这个结果就是二进制表示中1的个数。//求余统计二进制数中1的个数 int Count(unsigned char v) { int num=0;

xuguoli_beyondboy的专栏 1694

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

c语言 - 求一个整数储存在内存中的二进制中1的个数 / 求两个数二进制中不同位的个数

写一个函数返回参数二进制中 1 的个数。 本身与本身-1进行按位与&。循环进行,直到本数最终为0结束。 int main() { int num = 15; int i = 0; int count = 0; while (num) { count++; num = num&(num - 1); } printf("二进制序列的中1的个数为%d个\n", count); return 0; } ...

weixin_51136573的博客 179

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

求一个整数存储在内存中的二进制中1的个数

题目:求一个整数存储在内存中的二进制中1的个数 方法一: int num = 10; int count = 0;//计数 while (num) { if (num % 2 == 1) count++; num = num / 2; //等价于:num = num >> 1; } printf("二进制中1的个数为:%d\n", count); 这个是比较...

身在边城 心在编程 370

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

如何求二进制表示中“1”的个数

刚翻开《编程之美》, 中间就有一道我很眼熟的题,“求二进制中1的个数”。书中的题目描述如下:

AllZone 980

求二进制中 1 的个数之 HAKMEM 算法

方案一很简单,思想就是遍历一遍二进制串对 1 计数累加。方案二用的是 HAKMEM 算法,其背后有很复杂的数学推导,参见“最后那句话很受用:就是这些小得不能再小,基础得不能再基础的地方,决定了我们的水平。问题:求一个整数的二进制中 1 的个数。

wzy0623的专栏 768

位运算--求二进制中1的个数

位运算--求二进制中1的个数

triticale的博客 302

【算法】布赖恩·克尼根算法——天才算法求二进制中1的个数

题目背景 做leetcode第461题汉明距离的时候,发现一个很骚的求1的个数的算法,大呼牛逼! 其实题目本身思路不难,就是求异或然后算1个个数,只是没有想到还可以用这么骚的方式来求。 布赖恩·克尼根算法思路 是否可以像人类直观的计数比特为 1 的位数,跳过两个 1 之间的 0。例如:10001000。 这是布赖恩·克尼根位计数算法的基本思想。该算法使用特定比特位和算术运算移除等于 1 的最右比特位。 当我们在 number 和 number-1 上做 AND 位运算时,原数字 number 的最右边等于

Vincent Lai的博客 1149

C语言 求一个整数存储在内存中的二进制中的1的个数

通过以上,我们得知新的n和老的n相比,二进制数位上的1在逐渐的减少,并且是从右往左的方向以此减少,所以,我们可以得到一个规律,n = n & (n-1) 在不断的减少二进制位数上的1.而将一个整数各个位数分离的方法,我们可以采用先 %10 后 /10的方法,前者取出最低位的数字,后者去除最低位的数次,并加以循环,我们便可以得到一个整数的所有位数。通过题目我们可知,一个整数在内存中的二进制数位是这个整数的补码,因此求的是这个整数的补码中的1的个数。求一个整数存储在内存中的二进制中的1的个数。

2301_76445610的博客 329
上一篇: 优雅的全排列
下一篇: java 路径
easyjsf
博客等级 码龄17年 19粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值