Java中的Integer.bitCount浅析

文章目录

  • Java中的Integer.bitCount浅析
    • 问题
    • 思考
    • Integer.bitCount
      • 解释
      • 拓展

Java中的Integer.bitCount浅析

原文链接

问题

有一个整数x,我们需要统计该整数的二进制表示中包含的1的个数。这个也被称为汉明重量(Hamming weight)

例如,整数13的二进制表示是1101,其中有3个1,因此统计出的结果是3。

思考

看到这个问题的时候可能的想法就是遍历一遍这个二进制数位并统计结果。

对于统计32位的整数,下面是其中的一种做法:

 int bitCount(int x) {
        int count = 0;
        for (int i = 0; i < 32; i++) {
            int t = x & 1;
            //count+=t;
            if (t == 1) {
                count++;
            }
            x >>= 1;
        }
        return count;
    }

这种做法时间复杂度是O(n),n是二进制数的位数

Integer.bitCount

在Java中也有提供统计整数数位1数量的方法Integer.bitCount,下面是它的源码:

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;
    }

这个方法的代码第一眼看过去有点看不明白,但是我们可以很直观得看到这个方法里面并没有用到循环统计的方法,而是进行了几步位运算和算数运算就可以统计出来了,它是怎么做到的呢?

解释

直接举个例子:
对于数值为1822569234的32位整数,它的32位二进制表示为01101100101000100011001100010010,其中有131,计算过程如下:

对上图做出解释:

  1. 首先我们对二进制进行分组,每组一个比特位,这样一开始有32组,假设为每个分组编号,从左到右即分组1,分组2,......,分组32
  2. 接着将每两个相邻的组进行求和,即分组1和分组2分组3和分组4,…,分组31和分组32,这样我们就可以先求出每两位中1的数量

0+0=00(等于十进制0)
0+1=011+0=01(等于十进制1)
1+1=10(等于十进制2)

  1. 结果放到求和组的数位上,形成新的分组,每组中有两个比特位,这些组的值就是每两个比特位中1的数量,可以重新编号分组1,分组2......分组15,分组16
  2. 继续对相邻的组进行求和,组成每4个比特位为一组的分组,依此类推,直到每32位为一组就是答案了。

这个过程其实利用了分治的思想,将一个大的问题,分解为多个小的子问题,先对子问题进行求解,最后将子问题合并得出结果。

这个过程用代码写出来如下:

  static int bitCount(int x) {
        x = (x & 0b01010101010101010101010101010101) + ((x >> 1) & 0b01010101010101010101010101010101);
        x = (x & 0b00110011001100110011001100110011) + ((x >> 2) & 0b00110011001100110011001100110011);
        x = (x & 0b00001111000011110000111100001111) + ((x >> 4) & 0b00001111000011110000111100001111);
        x = (x & 0b00000000111111110000000011111111) + ((x >> 8) & 0b00000000111111110000000011111111);
        x = (x & 0b00000000000000001111111111111111) + ((x >> 16) & 0b00000000000000001111111111111111);
        return x;
    }

在大部分编程语言中要表示二进制数,可以在其前面加上0b0B前缀。

对于上述代码中,第一行就是求每个分组是1个比特位时,相邻分组的和。(x & 0b01010101010101010101010101010101)就是将分组1的值置零,保留分组2的值;分组3的值置零,保留分组4的值…依此类推。((x >> 1) & 0b01010101010101010101010101010101)
就是先将相邻分组中的第一个分组移到自己相邻的分组,即分组1的值移动到分组2分组2分组3,分组3分组4…,之后再将分组1,分组3,分组5…等置零,避免影响求和的结果。最后将(x & 0b01010101010101010101010101010101)((x >> 1) & 0b01010101010101010101010101010101)求和,就是第一次相邻分组的求和结果了,接着后面只是每个分组的比特位变多了,过程还是一样,最终得到32个比特位为一组时就是结果了。

这样的算法时间复杂度就是 O ( log ⁡ 2 n ) O(\log_2{n}) O(log2n),n是二进制数的位数

我们可以将上面的代码中的数值用十六进制表示:

static int bitCount(int x) {
        x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
        x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
        x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
        x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
        x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
        return x;
    }

和Java中的进行比较

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;
    }

很明显这个和Java中的bitCount还是不一样呀?其实Java中的bitCount是在这个代码基础上减少了一些不必要的运算的结果。

  1. 第一行 x = (x & 0x55555555) + ((x >> 1) & 0x55555555)其实可以等效于x = x - ((x >> 1) & 0x55555555),可以减少一次与运算
  2. 第二行,这个是求每个分组有2个比特位的时候的和,因为分组n分组n+1相加的结果最大是4,二进制表示即100,超过分组n+1的2比特位,所以只能保留原样
  3. 第三行,这个是求每个分组有4个比特位的时候的和,因为分组n分组n+1相加的结果最大时8,二进制表示即1000,没有超过分组n+1的4比特位,所以可以先对原来的数字和移位的数字进行求和,之后要对分组n的位置置零,避免对后面求和结果造成影响,也就是需要与上0x0F0F0F0F
  4. 第四行,这个是求每个分组有8个比特位的时候的和,因为分组n分组n+1相加的结果最大时16,二进制表示即10000,没有超过分组n+1的8比特位,所以可以和第三行那样先求和。而且32比特位中1的数量最大也就是32个,二进制表示即100000,只有6个比特位,所以结果也不会超过8个比特位,不需要对分组n置零,就是不需要与运算。
  5. 第五行,这个是求每个分组有16个比特位的时候的和,同第四行一样可以直接求。
  6. 最后的结果保存在低位的6个比特位置上,所以需要与上0b111111,换成十六进制就是0x3F

拓展

理解了32个数位的做法,那推广到64个数位也可以利用上述的思想。

下面是Java中Long.bitCount的源码

public static int bitCount(long i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x5555555555555555L);
        i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
        i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        i = i + (i >>> 32);
        return (int)i & 0x7f;
     }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

京东数据运营-京东数据平台-京东店铺数据分析-2023年10月京东烘干机品牌销售榜

鲸参谋监测的京东平台10月份烘干机市场销售数据已出炉&#xff01; 10月份&#xff0c;烘干机市场整体销售上涨。鲸参谋数据显示&#xff0c;今年10月份&#xff0c;京东平台上烘干机的销量将近5万件&#xff0c;环比增长约77%&#xff0c;同比增长约22%&#xff1b;销售额将近…

链表中的节点每k个一组翻转

描述 将给出的链表中的节点每 k 个一组翻转&#xff0c;返回翻转后的链表 如果链表中的节点数不是 k 的倍数&#xff0c;将最后剩下的节点保持原样 你不能更改节点中的值&#xff0c;只能更改节点本身。 示例1 输入&#xff1a; {1,2,3,4,5},2 返回值&#xff1a; {2,1,4,3,5} …

【性能测试】服务器常用的性能指标总结,一文概全...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 压测过程中&#…

用行云管家实现IT统一运维管理,提高运维效率

随着公司业务的不断壮大&#xff0c;需要用到的IT系统也越来越多&#xff0c;使用起来耗时耗力。因此实现IT统一运维管理已成为提高运维效率、降低成本、优化资源配置的重要途径。这里我们小编告诉您&#xff0c;用行云管家实现IT统一运维管理&#xff0c;提高运维效率&#xf…

【电路笔记】-电阻串联

电阻串联 文章目录 电阻串联1、概述2、电阻串联3、串联电阻电压4、电阻串联示例15、分压电路6、电阻串联示例27、电阻串联的应用8、总结 当电阻器以菊花链方式连接在一条线上时&#xff0c;电阻器被称为串联连接&#xff0c;从而导致共同电流流过它们。 1、概述 各个电阻器可以…

LeetCode(39)赎金信【哈希表】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 赎金信 1.题目 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字…

怎么检测电脑电源?电脑电源检测系统软件如何助力?

电源是电脑的重要组成部分&#xff0c;为电脑提供稳定电源&#xff0c;保证电脑正常工作。但是在电脑实际使用过程中总会遇到各种各样的问题和故障&#xff0c;比如无法开机&#xff0c;因此电脑电源检测是非常重要的测试内容。 如何测试电脑电源? 1. 用万用表检测 a. 将万用表…

【2021研电赛】基于机器视觉的智能水果质量监测系统

本作品介绍参与极术社区的有奖征集|分享研电赛作品扩大影响力&#xff0c;更有重磅电子产品免费领取! 团队介绍 参赛单位&#xff1a;华东理工大学 参赛队伍&#xff1a;Invictus Team II 指导老师&#xff1a;黄如 副教授 参赛队员&#xff1a;陈子健、管文范、冯默然 获奖情…

移动CRM可以在哪些场景下使用

最近杭州亚运会盛大举办&#xff0c;外国友人在打卡各地美食景点的同时也体会到了移动支付的乐趣。在智能手机全面普及的今天&#xff0c;移动CRM系统的应用也越来越广泛&#xff0c;移动CRM系统的应用场景有哪些&#xff1f;我们分享两个例子。 场景A&#xff1a; 李明是刚刚…

卡码网语言基础课 | 16. 出现频率最高的字母

目录 一、 哈希表 二、 编写解题 2.1 统计出现次数 2.2 解答 通过本次练习&#xff0c;将学习到C中哈希表的基础知识 题目&#xff1a; 给定一个只包含小写字母的字符串&#xff0c;统计字符串中每个字母出现的频率&#xff0c;并找出出现频率最高的字母&#xff0c;如果…

王者荣耀游戏制作

1.创建所需要的包 2.创建怪物类 bear package beast;import wangzherogyao.GameFrame;public class Bear extends Beast {public Bear(int x, int y, GameFrame gameFrame) {super(x, y, gameFrame);setImg("img/bear.jpg");width 85;height 112;setDis(65);}} b…

【目标跟踪】光流跟踪(python、c++代码)

文章目录 前言一、代码流程与思路二、python 代码2.1 代码详解2.2 完整代码 三、c 代码四、结果展示 前言 光流利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系&#xff0c;从而计算出相邻帧之间物体的运动信息的一种方法。…

初探HarmonyOS路由跳转

最近的鸿蒙新闻也是很大声势&#xff0c;鸿蒙的纯血版一出&#xff0c;各大互联网大厂都坐不住了&#xff0c;纷纷加入其中。这意味鸿蒙将来会取代大部分Android用户&#xff0c;这也是程序员的一篇大好前程。如今的Android开发行业已经夕阳西下了。 网上有关HarmonyOS的资料几…

雷达公式实现(matlab)

雷达公式实现 代码来源&#xff1a;《雷达系统分析与设计(MATLAB版)(第三版)》 function [snr] radar_eq(pt,freq,g,sigma,b,nf,loss,range) % This program implements Eq.(1.63) %% Inputs:% pt——峰值功率&#xff0c;W% freq——雷达中心频率&#xff0c;Hz% g——天线…

Linux 启动过程

linux启动步骤&#xff1a; <1>加电 <2>加载bios设置 <3>加载grup <4>加载内核系统到内存中 <5>加载配置文件 <6>加载内核模块 <7>完成相应的初始化工作和启动相应的服务 <8>启动系统进程 <9>出现登录界面 &l…

【傻瓜级JS-DLL-WINCC-PLC交互】6.​向PLC里面装载数据变量

思路 JS-DLL-WINCC-PLC之间进行交互&#xff0c;思路&#xff0c;先用Visual Studio创建一个C#的DLL控件&#xff0c;然后这个控件里面嵌入浏览器组件&#xff0c;实现JS与DLL通信&#xff0c;然后DLL放入到WINCC里面的图形编辑器中&#xff0c;实现DLL与WINCC的通信。然后PLC与…

低代码(Low Code)多项目开发平台核心能力解析

随着信息化的发展&#xff0c;企业基于互联网的业务不断得到扩展&#xff0c;作为业务协调的基础&#xff0c;企业信息系统的复杂程度也在不断提高。系统复杂了&#xff0c;就容易出问题&#xff0c;这就对系统的性能、可用性、可靠性和安全性都提出了新的要求&#xff0c;因此…

流程控制翻转学习

&#x1f4d1;前言 本文主要是【Python】——Python流程控制翻转学习的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每…

【Python基础】协程(迭代器、生成器、协程、gevent介绍)

&#x1f308;欢迎来到Python专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C、Linux系统编程、计算机网络、数据结构、Mys…

完美滤波器

完美滤波器 如下图所示&#xff0c;第 j j j级为输入图像&#xff0c;其中第 j − 1 j-1 j−1级为第 j j j级的尺寸减半的存在&#xff0c;直至为 1 1 1\times 1 11 的大小&#xff0c;这样的模式被称为图像金字塔 设原图像像素点个数为 N 2 N^2 N2&#xff0c;则图像金字塔的…
最新文章