当前位置: 首页 > news >正文

免费下载策划书的网站/南京seo优化公司

免费下载策划书的网站,南京seo优化公司,去哪里找做网站的,深圳注册公司个人数字证书文章目录前言预备知识位与运算&无符号右移>>>补码源码讲解基本原理两位二进制四位二进制32位的int源码详解总结前言 最近在刷力扣题时,刷到了一道统计数字二进制位里面1的数量的题,用常规方法做出来后,一看评论区才知道原来jav…

文章目录

    • 前言
    • 预备知识
      • 位与运算`&`
      • 无符号右移`>>>`
      • 补码
    • 源码讲解
      • 基本原理
        • 两位二进制
        • 四位二进制
        • 32位的int
      • 源码详解
    • 总结

前言

最近在刷力扣题时,刷到了一道统计数字二进制位里面1的数量的题,用常规方法做出来后,一看评论区才知道原来java的Integer类自带统计数字二进制表示里面1数量的方法——Integer.bitCount (int i)。但是Integer.bitCount (int i)的源码长得非常古怪,出于兴趣,我对它的源码好好研究了一番,并且特地在此记录。

这是Integer.bitCount (int i)的源码,后面会对这个源码进行详细解析:

public static int bitCount(int i) {// HD, Figure 5-2i = 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;
}

预备知识

首先在解析源码之前,需要对位运算和补码的知识有一定的了解。如果你对位运算和补码已经非常了解,可以跳过这一节。

这里用到的位运算只有两个&>>>

位与运算&

位与运算是对两个操作数按位取与。即只有两个操作数的第n位均为1时,结果的第n位才为1;两个操作数有一个的第n位为零,则结果的第n位为零。

例如,3&5==1:
3:   0B 0000 0000 0000 0000 0000 0000 0000 0011
5:   0B 0000 0000 0000 0000 0000 0000 0000 0101
3&5:  0B 0000 0000 0000 0000 0000 0000 0000 0001

根据位与运算的特点,我们可以将一个数字的某些位 置零,例如:
将31的低4位 置零:
31:    0B 0000 0000 0000 0000 0000 0000 0001 1111
-16:   0B 1111  1111 11111 1111 1111  1111 1111 0000
&:    0B 0000 0000 0000 0000 0000 0000 0001 0000

无符号右移>>>

java中的移位运算符有三种左移(<<)、右移(>>)和无符号右移(>>>)。 左移就是将二进制数左移指定位数,高位丢弃、低位补零

右移是将二进制数右移指定位数,低位丢弃,但是高位补数这里有一点不同。我们知道java中的数字都是以补码形式存储的,正数的补码是其本身,但是负数的补码是其对应正数的原码的每一位取反结果再加一,其中最高位是符号位,正数的符号为0,负数为1。

对一个数进行右移运算时,低位丢弃,高位补的是它的符号位的值,也就是说正数的高位补0、负数的高位补1。

根据上面的内容我们知道右移是与符号相关的,而无符号右移就是剔除了右移的符号相关性,无论是正数还是负数,执行无符号右移后,高位都补零

根据右移运算的特点,右移一位就相当于将原数除以2并且向下取整,15 >> 1 == 7-15 >> 1 == -8

无符号左移一位,如果是正数或者无符号数,则跟右移同效。对于负数,结果是其所对应正数除以2并且向下取整后的数字的补数,即n >>> 1 == nteger.MAX_VALUE - (-n >> 2),仅n为负数时成立!,至于为什么这样,跟补码表示有关,这里就不展开讲了。

三者区别如下:

移位方向补数
左移向左低位补零
右移向右高位补符号位的值
无符号右移向右高位补零

需要注意的是,位运算是直接对二进制位的操作,源码补码这些只是对一个二进制串解释方法的不同,不会改变这个二进制串本身。

补码

对于源码补码的知识,大家可参阅我的另一篇文章:原码、反码与补码

源码讲解

基本原理

我们知道一个二进制数的某一位的值只可能是0或者1,我们可以换一种理解方式,可以将零或者1理解为这个数字在这一位上1的数量,为零表示这一位没有1,为零表示这一位有一个1,基于这种理解,我们将这个二进制数每一位上的值(0或1)加起来,就是这个数的各个二进制位上1的数量,好好理解这句话后面有大用。

那么问题来了,如何将这个数每一位的值相加呢?

两位二进制

先考虑只有两位的情况,例如 n=0B11

首先请大家思考下对于两位十进制数n,我们是如何计算它的十分位和个分位的和的,是不是count = n/10 + n%10

那么对于两位二进制就可以写成count = n/2 + n%2。除以2是为了将低位的值舍弃,再将高位的值提取出来放到低位,这一步可以用n >>> 1替代,执行时低位自动被舍弃;取余2是为了消除高位的值,或者说将高位 置零,可以用n & 0B01替代。那么就可以改写成count = (n >>> 1) + (n & 0B01)

四位二进制

现在考虑4位的情况。以n = 0B1011为例。有了前面的基础,我们可以先将n两两分组,每组两位,n = 0B 10 11,利用前面两位的计算方法,将每一组的高位和低位相加,再将这两组的值加起来就是最终结果。

上面是位与上0B01,这里有两组,所以要位与上0B 01 01,即n1 = n & 0B0101,这样n1每一组的值就是n每一组低位里面1的数量。

前面是左移一位,因为低位的值会被自动丢弃。但是这里有两组,直接右移会将第一组低位的值放到第二组的高位,影响后续计算,所以执行>>1将高位放到低位后要再将每一组的高位 置零,即n2 = (n >>> 1) & 0B0101 ,这样n2每一组的值,就是n对应组的高位里面1的数量。

n3 = n1 + n2,现在n3的每一个分组的值就是n的每一个分组里面1的数量。将两个分组的值相加,过程与前面相同,现将n3的低分组置零提取出高位分组,再右移两位,n4 = (n3 & 0B1100) >> 2,的到高分组的值,只有两个分组,右移两位低分组的值被自动舍弃,所以简写成n4 = n3 >> 2;将n3的高位 置零,n5 = n3 & 0B0011,得到低位分组的值,n4 + n5就是n的所有位里面1的数量。

大家可以在草稿本上比划一下这个过程,说起来很复杂,其实写起来挺简单的。

32位的int

现在我们就可以通过上面的方式,求任何位数为2的幂的二进制数里面1的数量,对于java里面32位的int也不例外,并且只需要经过log⁡232\log_{2}{32}log232=5次上述过程即可。可以看出上面的过程其实就是一种分治的思想。

但是有一点要注意,java里面32位的int是有符号的,最高位为符号位,这也是为什么一直用的是无符号位移>>>而非 有符号位移>>。对于补码源码,对这个问题没有影响,因为为源码补码只是这个二进制串的整体所表示值的不同,对它本身每一位的值没有影响。

源码详解

源码:

public static int bitCount(int i) {// HD, Figure 5-2i = 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;
}

首先,
0x55555555 == 0B 0101 0101 0101 0101 0101 0101 0101 0101
0x33333333 == 0B 0011 0011 0011 0011 0011 0011 0011 0011
0x0f0f0f0f == 0B 0000 1111 0000 1111 0000 1111 0000 1111

按照之前的思路,第一步应该是将32的int两位一组,分成16个分组,将每个分组高位的值与低位的值相加得出该分组里面1的数量。

(i >>> 1) & 0x55555555这句话的作用很明确,就是将每个分组高位的值提取出来,可是i = i - ((i >>> 1) & 0x55555555);中间为什么是减号?

我们假设n为某一个分组的值,n有两个二进制位,假定两个二进制位的值分别为x,y,则n = 2x + y,那么n - x == x + y,即高位与低位之和。

上面(i >>> 1) & 0x55555555相当于是求出了x,所以i = i - ((i >>> 1) & 0x55555555);i = (i & 0x55555555) + ((i >>> 1) & 0x55555555);等价。

第二句话就是常规操作了,i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);,将16个分组分成8组,每组的高位分组与低位分组值相加,就是每组(4位)里面1的数量。

第三句话又张的不一样了,i = (i + (i >>> 4)) & 0x0f0f0f0f;。我们知道现在有8个分组,每个分组4位,8个分组两两一组后为4个组,每组八位最多有8个1,而低分组的4位可存储的最大值为15>8,不会溢出。所以可以先加再置零。

第四句话是一样的道理,i = i + (i >>> 8);,4个分组分成两组,低分组的八位足以容纳32,所以不必在将第一组置零。

第五句话跟第四句话一样,不再过多解释。

最后一句话,取低6位,五位最大31,六位最大63。

总结

可以看出jdk源码对上面的算法做了不少细节上的优化,这需要对位运算有极高的理解,将每一步都优化到极致,短短的六句话就蕴含了这么多细节。相信将这个源码完全看懂后,你会对位运算和二进制有更加深刻的理解。

http://www.lbrq.cn/news/1360009.html

相关文章:

  • 网站建设客户沟通模块/seo技巧seo排名优化
  • 雄安移动网上营业厅/开鲁网站seo转接
  • 怎么做淘宝一样的网站/拼多多关键词优化是怎么弄的
  • 家具网站建设方案/黑马培训价目表
  • layui做网站前端/网页广告调词平台多少钱
  • php多语言网站怎么做/深圳网络营销和推广渠道
  • 做网站一个月可以赚多少/360优化大师官方下载
  • 专用车网站建设哪家好/深圳宝安seo外包
  • 南宁网站建设gxskm/大兵seo博客
  • 贵州做网站的/新媒体seo指的是什么
  • 做去态网站要学java吗/中国新闻网发稿
  • 佛山网站常见的问题/网推放单平台
  • 零基础网站建设教程/seo推广有哪些公司
  • 飞凡 做电商网站/电商网站大全
  • 江苏省建设厅政务网站/优化网站标题名词解释
  • 坪地网站建设/湘潭seo公司
  • 网站怎么做话术/宣传网页制作
  • 做网站一般用什么服务器/seo软件定制
  • 中韩双语网站制作价格/北京培训机构
  • wordpress企业网站源码/安卓优化大师手机版
  • 成都市建设招标网站/网站seo分析
  • 美食网站设计风格/seo网站推广教程
  • 我在某赌博网站做代理/黄页网络的推广网站有哪些类型
  • 寿光市建设局网站/百度有哪些app产品
  • 企业app软件定制开发靠谱吗/宁波seo公司排名榜
  • 网站的优化从哪里进行/app推广一手单
  • 承德网站设计公司/合肥seo网站建设
  • 深圳本地专业网站设计/品牌营销推广代运营
  • 免费做公司网站能在百度上搜索的到/友情链接有哪些展现形式
  • 网站建设思维/博客网
  • Rust进阶-part4-智能指针2
  • linux定时器管理 timer_*系统调用及示例
  • 《Node.js与 Elasticsearch的全文搜索架构解析》
  • 深入浅出 RabbitMQ-路由模式详解
  • Claude Code六周回顾
  • python包管理器uv踩坑