Java.Integer.bitCount(int)源码解析

news2025/1/13 17:31:49

bitCount

  • 前言
  • 一、由易到难,头脑热身
  • 二、简单优化,一题多解
  • 三、分治优化
  • 四、bitCount(int)源码优化
  • 总结
  • 参考文献

前言

如何求解一个二进制中1的个数?有常规的O(N)法,还有基于分治的O(logN),即Java的bitCount(int)方法。
对于bitCount(int)源码,是已经优化过的代码,已经看不到原始的分治逻辑,显的很难。但从分治原理到优化,思路非常简单,感受分治的魅力,感受挖掘规律进行优化的魅力。
所有的困难都是由简单知识点结合内部逻辑联系组合而成!

一、由易到难,头脑热身

如何求二进制中1的个数?常规的方法就是对每位二进制进行判定累计。

 	public int hammingWeight(int n) {
        int cnt = 0;
        for(int i = 0;i < 32;i++){
            if((1 << i & n) != 0) ++cnt;
        }
        return cnt;
    }

二、简单优化,一题多解

如果二进制计算基础,即常见位运算,那基本知道如何快速将最后一个1消掉。n - 1会导致二进制最后一个1被借用,其后的0全部变为1,如8 = 0x1000,8 - 1 = 7 = 0x0111; 那么n & (n - 1)就能把最后一个1消掉,如0x1000 & 0x0111 = 0x0000;

	public int hammingWeight(int n) {
        int cnt = 0;
        while(n != 0){
            ++cnt;

            n = n & (n - 1);
        }
        return cnt;
    }

这样就能减少判定次数,而且没有if判定。

三、分治优化

15 = 1111,如何分治计算1的个数,直接统计1的个数即可,即不断做加法即可。
在这里插入图片描述

提取关键问题,如何让前一位和后一位做加法呐
直接将二进制无符号右移一位,前后两位不就对齐了吗?再用0101…来将左边多余的二进制抹除,再进行最终的加法运算。
0101 = 5,所以需要用5来抹除多余的1.

n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);

统计了1位,接下来统计2位,再统计4位,继续统计8位 / 16位,都是同样的道理,直接通过无符号右移不同的位数进行加法统计即可。

public int hammingWeight(int n) {
		// 用0101来抹除多余的1 + 右移1位对齐。
        n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
        // 用0011来抹除多余的1 + 右移2位对齐。
        n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
        // 用00001111来抹除多余的1 + 右移4位对齐。
        n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
        // 用0000000011111111来抹除多余的1 + 右移8位对齐。
        n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
        // 用00000000000000001111111111111111来抹除多余的1 + 右移16位对齐。
        n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
        
        return n;
    }

四、bitCount(int)源码优化

上面就是bitCount的分治原理,再深入挖掘二进制的规律,挖掘计算中的个性,来做一个优化。

  1. 用0101来抹除多余的1 + 右移1位对齐。
    n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
    对于两位二进制来讲,0x11 - 0x01 = 0x10 = 2,表示有2个1,0x10 - 0x01 = 0x01 = 1表示有1位二进制,就是这么巧!
    0x11 >>> 1 = 0x01;0x10 >>> 1 = 0x01;
    对于第2为为0的情况,自然不用管,毕竟0x01 - 0x00 = 1;0x00 - 0x00 = 0;
    所以可以用减法,少一次与运算,n = n - ((n >>> 1) & 0x55555555)

  2. 用0011来抹除多余的1 + 右移2位对齐。
    n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
    无法优化,没有二进制规律,而且最多4个1需要3位二进制表示。

  3. 用00001111来抹除多余的1 + 右移4位对齐。
    n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
    这里需要统计的是1byte中1的个数,而1的个数最多有8个,4位二进制完全够用了,所以可以先做加法运行,再对多余的0进行抹除,来减少一次运算。即n = (n + (n >>> 4)) & 0x0f0f0f0f;

第4/5点同理,但是从第4点开始,就有8位的空间来统计二进制数,而int只有32位,只需6个bit可以完成统计,所以可进一步优化!
先不管多余的二进制(未对齐的错误运算),最后统一把其抹除,只用6bit即可,所以用0x111111 = 0x3f来抹除多余的二进制。

疑问:为什么4位时不行?而8/16位可以呐?还是回归到6bit足够表示32位二进制个数了,4bit不行,下次运算时,紧挨着的2bit被运算,而且还抹不掉这个未对齐的错误运算!

bitCount源码,即最终优化过的代码,

/**
     * Returns the number of one-bits in the two's complement binary
     * representation of the specified {@code int} value.  This function is
     * sometimes referred to as the <i>population count</i>.
     *
     * @param i the value whose bits are to be counted
     * @return the number of one-bits in the two's complement binary
     *     representation of the specified {@code int} value.
     * @since 1.5
     */
    @HotSpotIntrinsicCandidate
    public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

总结

1)分治统计,从O(N)降到O(logN)。
2)从易到难,一步步挖掘内在规律和个性,一步步优化,完成经典之作。
3)所有困难都是由简单知识点和它们之间的内在逻辑联系构成!

参考文献

[1] LeetCode 位1的个数
[2] bitCount 源码解析
[3] JDK 12

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/45199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CockroachDB-备份与恢复(1)备份架构

本文知识点来源于官网地址https://www.cockroachlabs.com/docs/stable/backup-architecture.html CockachDB备份以作业的方式操作&#xff0c;作业是可能跨越多个SQL会话的长期运行操作。与常规SQL语句不同&#xff0c;BACKUP语句在作业工作流中执行。备份任务有四个主要阶段:…

CDMP考试时间与报名方式

CDMP“数据管理专业人士认证”证书国际通用&#xff0c;行业认可度极高&#xff0c;是一项涵盖学历教育、工作经验和专业知识考试在内的综合资格认证&#xff0c;也是 目前全球唯一数据管理方面权威性认证 。CDMP考试时间是什么时候&#xff1f;怎样报名&#xff1f;今天小编来…

C语言 =(按位与后赋值)^=(按位异或后赋值) |=(按位或后赋值)

&(按位与后赋值&#xff09; x 0x02; x & 0x01; 按位与后的结果为&#xff1a;0x00 x 0x02; x & 0x01; 字符 & 的最早历史可以追溯到公元1世纪&#xff0c;最早是拉丁语et (意为and)的连写。最早的 & 很像 E 和 T 的组合&#xff0c;随着印刷技术的发展&…

[附源码]计算机毕业设计springboot付费自习室管理小程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计Django宠物领养与物品捐赠小程序

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

文件上传漏洞详解

文件上传漏洞详解1.文件上传漏洞1.1.文件上传漏洞定义1.2.文件上传漏洞原理1.3.文件上传思路1.3.1.常规类1.3.2.cms类1.3.3.编辑类1.3.4.其他类/CVE1.4.web界面存在的风险点1.5.文件上传实战思路2.文件上传绕过分类2.1.JS类防护2.1.1.前端验证  2.1.1.1.基本概念  2.1.1.2.…

普元中间件Primeton AppServer6.5部署SuperMap iServer

本文使用Windows环境普元中间件Primeton AppServer6.5&#xff08;以下简称PAS&#xff09;部署SuperMap iServer 一、部署前准备 本文使用SuperMap iServer 11.0.1&#xff08;10.2.1版本同理可以使用&#xff09; supermap-iserver-11.0.1-war.zip 安装完成的普元中间件PAS…

EEMD分解如何对IMF分量进行显著性检验?

EEMD简介 集合经验模态分解 (EEMD)方法是一种时间上局部的自适应时间序列分析技术, 适合于分析非线性、非平稳的时间序列. EEMD 方法改进了经验模态分解 (EMD)的 模态混叠问题 。EEMD 方法是利用多次测量取平均值的原理,通过在原数据中加入适当大小的白噪音来模拟多次观测的情…

【服务器数据恢复】hp服务器raid5磁盘掉线导致raid5不可用的数据恢复案例

服务器数据恢复环境&#xff1a; 惠普ML系列某型号塔式服务器&#xff0c;5块SAS硬盘组建raid5磁盘阵列。 服务器故障&分析&#xff1a; 服务器中的一块硬盘掉线&#xff0c;由于磁盘阵列的冗余特性&#xff0c;服务器正常运行&#xff0c;用户没有察觉。直到另外一块硬盘掉…

JVM垃圾回收算法

Java有着自己一套的内存管理机制&#xff0c;不需要开发者去手动释放内存&#xff0c;开发者只需要写好代码即可&#xff0c;运行过程中产生的垃圾都由JVM回收。那JVM都是用哪些算法进行垃圾回收呢&#xff1f; 标记-清除(Mark-Sweep)算法 标记-清除(Mark-Sweep)算法是最早出…

分布式 ID 生成系统 Leaf 的设计思路,源码解读

什么是分布式ID&#xff1f; ID 最大的特点是 唯一 而分布式 ID&#xff0c;就是指分布式系统下的 ID&#xff0c;它是 全局唯一 的。 为啥需要分布式ID呢&#xff1f; 这就和 唯一 息息相关了。 比如我们用 MySQL 存储数据&#xff0c;一开始数据量不大&#xff0c;但是业…

别再纠结线程池大小 + 线程数量了,没有固定公式的

可能很多人都看到过一个线程数设置的理论&#xff1a; CPU 密集型的程序 - 核心数 1I/O 密集型的程序 - 核心数 * 2 不会吧&#xff0c;不会吧&#xff0c;真的有人按照这个理论规划线程数&#xff1f; 线程数和CPU利用率的小测试 抛开一些操作系统&#xff0c;计算机原理不…

水资源税取水计量监管系统 取用水户水量在线监测平台 水资源远程实时监控管理系统

平升电子水资源税取水计量监管系统/取用水户水量在线监测平台/水资源远程实时监控管理系统适用于水资源管理部门对地下水和地表水的用水量、水位、水质进行监测&#xff0c;还可扩展远程或自动控制泵/闸/阀实现用水量控制。系统帮助管理部门掌握所辖区域内水资源取用水情况&…

打电话用蓝牙耳机什么牌子好?打电话清晰的蓝牙耳机推荐

随着蓝牙耳机的普及&#xff0c;我们可以享受到沉浸式的音乐。在不打扰任何人的情况下&#xff0c;尽情的享受&#xff0c;使用蓝牙耳机有时候避免不了来电&#xff0c;为了保证通话的清晰&#xff0c;许多人在选购的时候也会更加的看重麦克风&#xff0c;下面小编整理了几款打…

如何使用JMeter操作Elasticsearch

JMeter是Apache组织基于Java开发的压力测试工具&#xff0c;用于对软件做压力测试&#xff0c;Elasticsearch是一个分布式、高扩展、高实时的搜索与数据分析引擎(简称ES)&#xff0c;下面来展示最基本的用JMeter操作ES示例。 打开JMeter工具&#xff0c;在测试计划下添加“线程…

【金万维】使用天联高级版登录U8,进行凭证打印操作。

【操作步骤】 通过“天联高级版客户端”登录 U8&#xff0c;打印凭证步骤&#xff1a; 第一步&#xff1a;首先查看一下天联高级版客户端的打印参数是否如下图所示。 &#xff08;一般软件初次安装后&#xff0c;默认即可。&#xff09; 第二步&#xff1a;进入U8后&#xff0…

web概述20

MVC模式 MVC全名是Model View Controller是模型视图控制器的缩写&#xff0c;是一种软件设计典范&#xff0c;是一种架构型的模式&#xff0c;本身不引入新功能&#xff0c;只是帮助将开发的结构组织的更加合理。 它使用一种业务逻辑、数据、界面显示分离的方法&#xff0c;将…

麦芽糖-聚乙二醇-顺铂 cisplatin-PEG-maltose

麦芽糖-聚乙二醇-顺铂 cisplatin-PEG-maltose 中文名称&#xff1a;麦芽糖-顺铂 英文名称&#xff1a;maltose-cisplatin 别称&#xff1a;生物素修饰麦芽糖 生物素-麦芽糖 麦芽糖-聚乙二醇-顺铂 cisplatin-PEG-maltose 顺铂-PEG-麦芽糖 纯度&#xff1a;95% 存储条件…

电动车充电费到了涨价的时候了,低能源使用成本正在成为过去

电动汽车以省钱成为各个新能源汽车企业吹嘘的宣传点&#xff0c;然而电动汽车车主如今正面临公共充电桩短缺的问题&#xff0c;公共充电桩的建设跟不上电动汽车增长的速度&#xff0c;导致电动汽车车主充电难问题日益突出&#xff0c;解决这个问题就只能通过涨价来解决供应短缺…

ln命令应用

记录&#xff1a;352 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用ln命令创建软链接(symbolic links)和硬链接(hard links)。解决&#xff1a;Too many levels of symbolic links。 版本&#xff1a; 操作系统&#xff1a;CentOS 7.9 1.命令应用 (1)目录创建软…