拜托,面试别再让我数1了!!!

简介: 面试中,除了TopK,是否被问过:求一个正整数的二进制表示包含多少个1?

面试中,除了TopK,是否被问过:求一个正整数的二进制表示包含多少个1?

画外音:姊妹篇《拜托,面试别再问我TopK了!!!》。

例如:

uint32_t i=58585858;

i的二进制表示是:

0000 0011 0111 1101 1111 0011 0000 0010

于是,i的二进制表示包含15个1。

到底有几种方法,这些思路里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。

一、位移法

思路:既然输入n是uint32,每次取n的最低位,判断是不是1,位移32次,循环判断即可。

伪代码:

do{

    if ((n&1)==1){

       result++;

    }

    n>>= 1;

    i++;

} while(i<32);

分析:不管n的二进制表示里包含多少个1,都需要循环计算32次,比较耗时。有没有可能,每次消除掉一个1,这样来降低计算次数呢?

二、求与法

观察一下n与n-1这两个数的二进制表示:

最末位一个1会变成0

最末位一个1之后的0会全部变成1

其他位相同

栗子:

           x = 1011 0000

         x-1= 1010 1111

x & (x-1) = 1010 0000

于是,n&(n-1)这个操作,可以起到“消除最后一个1”的功效。

思路:逐步通过n&(n-1),来消除n末尾的1,消除了多少次,就有多少个1。

伪代码:

while(n){

   result++;

   n&=(n-1);

}

分析:这个方法,n的二进制表示有多少个1,就会计算多少次。总的来说,n的长度是32bit,如果n的值选取完全随机,平均期望由16个1构成,平均下来16次,节省一半的计算量。

画外音:校招时,我问过这样的面试题,“如何快速判断一个正整数是不是2的x次幂”,巧妙解法是

return !(n&(n-1));

即,如果n是2的x次幂,二进制表示只有一个1。

三、查表法

空间换时间,是算法优化中最常见的手段,如果有相对充裕的内存,可以有更快的算法。

思路:一个uint32的正整数n,一旦n的值确定,n的二进制表示中包含多少个1也就确定了,理论上无需重新计算:

1的二进制表示中包含1个1

2的二进制表示中包含1个1

3的二进制表示中包含2个1

…

58585858的二进制表示中包含15个1

...
提前计算好结果数组:

result[1]=1;

result[2]=1;

result[3]=2;

…

result[58585858]=15;

…

伪代码:

return result[n];

查表法的好处是,时间复杂度为O(1),潜在的问题是,需要很大的内存。

内存分析:

假如被分析的整数是uint32,打表数组需要记录2^32个正整数的结果。

n的二进制表示最多包含32个1,存储结果的计数,使用5个bit即可。

故,共需要内存2^32 * 5bit = 2.5GB。

画外音:5个bit,能表示00000-11111这32个数。帮忙看下,算错了没有,上一篇文章bit和Byte算错了8倍。

四、二次查表法

查表法,非常快,只查询一次,但消耗内存太大,在工程中几乎不被使用。

算法设计,本身是一个时间复杂度与空间复杂度的折衷,增加计算次数,往往能够减少存储空间。

思路:

(1)把uint32的正整数n,分解为低16位正整数n1,和高16正整数n2;

(2)n1查一次表,其二进制表示包含a个1;

(3)n2查一次表,其二进制表示包含b个1;

(4)则,n的二进制表示包含a+b个1;

伪代码:

uint16 n1 = n & 0xFFFF;

uint16 n2 = (n>>16) & 0xFFFF;

return  result[n1]+result[n2];

问题来了:增加了一倍的计算量(1次查表变2次查表),内存空间是不是对应减少一半呢?

内存分析:

被分析的整数变成uint16,打表数组需要记录2^16个正整数的结果。

n1和n2的二进制表示最多包含16个1,存储结果的计数,使用4个bit即可。

故,共需要内存2^16 * 4bit = 32KB。

画外音:帮忙看下,算错了没有。

好神奇!!!

计算量多了1次(1倍),内存占用量却由2.5G降到了32K(1万多倍),是不是很有意思?

五、总结

数1,不难;但其思路有优化过程,并不简单:

(1)位移法,32次计算;

(2)n&(n-1),能消除一个1,平均16次计算;

(3)查表法,1次查表,2.5G内存;

(4)二次查表法,2次查表,32K内存;

知其然,知其所以然。

思路比结论重要。

希望大家对“数1”有新的认识,谢转。

作业题:升级为4次查表,需要使用多少内存呢?

image.png

架构师之路-分享可落地的架构文章

目录
相关文章
|
Java 大数据 API
用上myexcel,百万数据导出也不怕
用上myexcel,百万数据导出也不怕
511 0
|
Java 应用服务中间件 Maven
Springboot项目将jar包修改为war包操作步骤
Springboot项目将jar包修改为war包操作步骤 文章目录 Springboot项目将jar包修改为war包操作步骤 1.修改jar为war包形式 2.去除Spring Boot内置Tomcat 3.增加Tomcat启动插件 4.使用maven编译程序
1008 0
Springboot项目将jar包修改为war包操作步骤
|
传感器 物联网 大数据
[总结]蓝牙各个版本的关系和区别
[总结]蓝牙各个版本的关系和区别
2324 0
|
网络协议 搜索推荐 Linux
信息搜集工具:Maltego
信息搜集工具:Maltego
322 0
|
机器学习/深度学习 人工智能 数据可视化
21款改变世界的AI工具:释放无限创意!
本文收集了21款令人惊叹的人工智能工具,每一款工具都为用户带来了创新与便捷。从数据分析、文档编写、语音克隆到图像升频,这些工具涵盖了多领域的应用。无论是自动化工作流的 n8n,还是开源替代 Notion 的 AppFlowy,这些工具都旨在通过 AI 提高生产力、简化流程,甚至激发更多创意。本文详细介绍了每个工具的用途、功能特点以及使用场景,是你探索 AI 世界的必备指南。
761 0
|
人工智能 运维 监控
大数据在城市智能轨道交通的应用
随着城市轨道交通体系建设的逐渐普及,我国城市轨道交通网路愈加复杂,接入站点、旅客运输量等不断提高,为城市轨道交通的运行带来了一定的压力。
大数据在城市智能轨道交通的应用
|
机器学习/深度学习 数据采集 算法
不平衡数据集分类实战:成人收入数据集分类模型训练和评估(一)
不平衡数据集分类实战:成人收入数据集分类模型训练和评估(一)
865 0
不平衡数据集分类实战:成人收入数据集分类模型训练和评估(一)
|
索引
【仿真建模】第二课:AnyLogic入门基础课程 - 行人仿真空间逻辑讲解
每个智能体都有个属性index,从0到n,所以我们创建多层建筑没必要一层一层建立,只需要把一层建筑封装成一个类,然后拖动出来,使用类似for循环的机制,去复制即可。指定初始位置,index*20,代表每层高度为20,通过index自增的索引进行高度自增。这里要注意,一定要将矩形墙的中心对准原点,因为在复制封装好的类时,它会以原点为参考原点。设置墙的颜色(大家自己选一个颜色即可),透明度设置为100(主要是为了能看清建筑内部)在工程面板找到刚新建的层,然后修改dZ为40(高度为40)
1071 0
【仿真建模】第二课:AnyLogic入门基础课程 - 行人仿真空间逻辑讲解
|
Ubuntu 关系型数据库 MySQL
|
SQL 运维 算法
DTCC 2020 | 阿里云梁高中:DAS之基于Workload的全局自动优化实践
第十一届中国数据库技术大会(DTCC2020),在北京隆重召开。在12.23日性能优化与SQL审计专场上,邀请了阿里巴巴数据库技术团队高级技术专家梁高中为大家介绍DAS之基于Workload的全局自动优化实践。 SQL自动优化是阿里云数据库自治服务重要自治场景之一,该服务支撑阿里巴巴集团全网慢SQL的自动优化,目前已累计自动优化超4900万慢SQL。阿里在构建这一能力过程中有经验也有教训,期望从基于Workload的全局优化能力构建历程、智能化自动优化闭环实践两个方面和大家分享。
4708 2
DTCC 2020 | 阿里云梁高中:DAS之基于Workload的全局自动优化实践