Integer中的奇妙位运算
Integer中的奇妙位运算
参考资料
https://segmentfault.com/a/1190000015763941
highestOneBit(int i)
函数的作用是获得传入参数的最高位的1,对于正数来说返回值为小于i的最大二次幂,对于负数来说永远是负数的最大值即-2^31
例如:7=0000 0111(省略前24位0)那么函数的返回值为 0000 0100=4
暴力法
通常来说最直观的做法就是暴力法,我一个一个数不就好了
//一位一位取就是了
public int heigestOneBit(int i){
int res=1;
if (i<0)return Integer.MIN_VALUE;
while(i!=0){
if (i!=1){
res*=2;
}
i/=2;
}
return res;
}
位运算
看看JDK如何利用更加高效的位操作实现这一个函数
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
为什么JDK一通位操作就把最高位取出来了呢?非常的巧妙啊,举例说明,看以下就知道了
//以下以数字为例,其中x表示0或者1不影响结果,1-最高位的1
i 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx
i>>1 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx
i|=(i>>1) 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx
i 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx
i>>2 0000 0000 11xx xxxx xxxx xxxx xxxx xxxx
i|=(i>>2) 0000 0011 11xx xxxx xxxx xxxx xxxx xxxx
i 0000 0011 11xx xxxx xxxx xxxx xxxx xxxx
i>>4 0000 0000 0011 11xx xxxx xxxx xxxx xxxx
i|=(i>>4) 0000 0011 1111 11xx xxxx xxxx xxxx xxxx
i 0000 0011 1111 11xx xxxx xxxx xxxx xxxx
i>>8 0000 0000 0000 0011 1111 11xx xxxx xxxx
i|=(i>>8) 0000 0011 1111 1111 1111 11xx xxxx xxxx
i 0000 0011 1111 1111 1111 11xx xxxx xxxx
i>>16 0000 0000 0000 0000 0000 0011 1111 1111
i|=(i>>16) 0000 0011 1111 1111 1111 1111 1111 1111
i 0000 0011 1111 1111 1111 1111 1111 1111
i>>>1 0000 0001 1111 1111 1111 1111 1111 1111
i-(i>>>1) 0000 0010 0000 0000 0000 0000 0000 0000
看完上面的简单分析应该就知道JDK如何实现的了,简单来说就是把第一个1不断往后移动,使得从第一个1之后的所有比特位都为1,此时减去右移一位的值,也就是减去后面所有的1代表的值,此时自然只剩下第一个1了,可以说非常的巧妙了
bitCount(int i)
该方法的作用是统计一个整数的二进制表示形式中1的个数,没记错的话这其实也是leetcode中的一道题
首先还是我们自己来思考一下如何实现:
暴力法
一个bit一个bit计数
public static int bitCount(int i){
//暴力法
int count=0;
while(i!=0){
if ((i&1)==1){
count++;
}
i=i>>>1;
}
return count;
}
位运算优化一下
试想对于二进制 100,1的个数为1,按照暴力法需要3次才能统计出来,怎么样一次统计出来呢,也就是怎么一次就把100变成0呢?
对于1xxx这样的数字,x代表0,以100为例,100-1=011,而100&011恰好为0,能做多少次这样的运算,它就有多少位1,代码如下
public static int bitCount(int i){
//位运算优化
int count=0;
while(i!=0){
i=i&(i-1);
count++;
}
return count;
}
到这里,有多少位的1,就统计多少次,貌似看起来已经还算不错了
JDK的位运算
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;
}
Superise Mother Fxxk!这是在干什么?首先解释一下总体的思想
//要统计如下二进制的:1001 1010 1010 1010 的1的位数
//JDK是这样做的
先每两个bit统计有多少个1,然后就保存在二进制的本地
10 01 10 10 10 10 10 10
01 01 01 01 01 01 01 01
然后再统计连续四个bit有多少个1,然后保存在本地
0010 0010 0010 0010
再统计8个bit有多少个1,保存在本地
0000 0100 0000 0100
然后再统计每16个比特有多少个1,保存再本地
0000 0000 0000 1000 ==8总共8个1
有了整体的算法思想,来看看这几个奇怪的数字0x55555555、0x33333333、0x0f0f0f0f
他们对应的二进制如下:
0x55555555 01010101010101010101010101010101
0x33333333 00110011001100110011001100110011
0x0f0f0f0f 00001111000011110000111100001111
针对0x55555555来看看效果,怎么把两个相邻bit位中的1存储下来,
//以12345为例
12345 0000 0000 0000 0000 0011 0000 0011 1001
0x55555555 0101 0101 0101 0101 0101 0101 0101 0101
12345&
0x55555555 0000 0000 0000 0000 0001 0000 0001 0001
//可以看到相当于把两个相邻的比特位的后一位的1全部取出来了
12345>>>1 0000 0000 0000 0000 0001 1000 0001 1100
0x55555555 0101 0101 0101 0101 0101 0101 0101 0101
12345>>>1
&0x55555555 0000 0000 0000 0000 0001 0000 0001 0100
//可以看到相当于把两个相邻的比特位的前一位的1全部取出来了
12345 00 00 00 00 00 00 00 00 00 11 00 00 00 11 10 01
last 1 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 01
first 1 00 00 00 00 00 00 00 00 00 01 00 00 00 01 01 00
last1+fisrt1 00 00 00 00 00 00 00 00 00 10 00 00 00 10 01 01
//可以看到两位中的1的数量已经用两个bit来保存了
算法实现如下:
public static int bitCount(int i) {
i = (i & 0x55555555) + ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f);
i = (i & 0x00ff00ff) + ((i >>> 8) & 0x00ff00ff);
i = (i & 0x0000ffff) + ((i >>> 16) & 0x0000ffff);
return i;
}
JDK再做点优化即可
- 第一步,对于 11 这两个比特位来说,用 01 + 01 的方式可以得到 10 也可以用 11 - 01 的方式,所以可以少做一次位运算
- 第三步,统计两个 4个连续bit 中1的个数,例如 0011 0011 -> 0000 1100 ,而8个bit最多就8个1,用4个字节就可以表示了,所以可以先加,0011 1100,再消除,0000 1100,第四步第五步同理,前面的留着不管,最后返回的时候把前面的bit置0
- return i & 0x3f,32位的int,最多就32个1,所以取后六位即可表示所有的可能
上述两者的使用
上面两个方法均在HashMap(JDK7实现)中的roundroundUpToPowerOf2方法中被调用,HashMap的分析详见https://www.cnblogs.com/danzZ/p/14075147.html,还是非常有意思的
Integer中的奇妙位运算的更多相关文章
- 【转】Cocoa中的位与位运算
转自:http://www.tuicool.com/articles/niEVjy 介绍 位操作是程序设计中对位模式或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常 ...
- Python语言中的按位运算
(转)位操作是程序设计中对位模式或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加 ...
- C++中巧妙的位运算
位运算要多想到与预算和异或运算,并常常将两个数对应位上相同和不同分开处理 一.x&(x-1)消除x二进制中最右边的一个1. 这个比较厉害,比如统计某个 二.与和异或的巧妙结合的思想 与运算可以 ...
- 状态压缩中常用的位运算(DP)
面对位运算,一直很无感...可能数学太差,脑洞太小. 1.首先是最基本的: 与&,或|,非~,异或^. 2.获取一个或者多个固定位的值: 假设 x = 1010(二进制),我们要取左数第二位的 ...
- Java中的按位运算
博客大搬家. 一.位运算符简介: 1.按位与&.如果两个整形数据 a.b 对应位都是1,则结果位才为1,否则为0,(int 最大值0x7fffffff ): int a = 0x7ffffff ...
- C语言中的重要位运算
1. 常用的等式 :-n = ~(n-1) = ~n + 1. 2. 获取整数n的人进制形式中的最后1个,也就是只保留最后一个1,其余的全部置位0,如1000 0011 ---> 0000 0 ...
- 嵌入式C语言位运算之清位置位
如题,在嵌入式开发中,掌握位运算是节省开发时间和提高开发效率的一种高效方式. 我们不得不去熟悉如何快速掌握位运算这种高效的技巧,接下来看看程序.. #include <stdio.h> # ...
- jave 逻辑运算 vs 位运算 + Python 逻辑运算 vs 位运算
JAVA中&&和&.||和|(短路与和逻辑与.短路或和逻辑或)的区别 博客分类: 面试题目 Java.netBlog 转自 :http://blog.csdn.net/web ...
- JAVA程序开发按位运算的记录
忘记在哪里看到一个面试题:把int a,b的值互换,不能使用临时变量.刚开始完全懵逼,脑子里面全是浆糊,不知道如何下手.查看答案后猛地一惊,心想居然还有这种操作,真是叹为观止,真的感觉自己的基础是如此 ...
随机推荐
- Error: pg_config executable not found.
pip 安装 psycopg2 安装及错误 现象: Error: pg_config executable not found. Please add the directory containing ...
- [P2114] [NOI2014]起床困难综合症 (位运算)
题面 传送门:https://www.luogu.org/problemnew/show/P2114 Solution 一道很有意思的位运算题. 要做这一题,我们首先得了解一个很重要的特点 位运算过程 ...
- 小白如何学习PyTorch】25 Keras的API详解(下)缓存激活,内存输出,并发解决
[新闻]:机器学习炼丹术的粉丝的人工智能交流群已经建立,目前有目标检测.医学图像.时间序列等多个目标为技术学习的分群和水群唠嗑答疑解惑的总群,欢迎大家加炼丹兄为好友,加入炼丹协会.微信:cyx6450 ...
- 1到n整数中1出现的次数
1到n整数中1出现的次数 题目描述 输入一个整数n, 求1~n这n个整数的十进制表示中1出现的次数. 例如, 输入12, 1~12这些整数中包含1的数字有1, 10, 11和12, 1一共出现了4次 ...
- 处理request信息的ngx_http_process_request
在处理完http的头部信息后 然后在 处理request-body信息ngx_http_process_request-------- -----------ngx_http_process_req ...
- 广度优先遍历&深度优先遍历
一.广度优先算法BFS(Breadth First Search) 基本实现思想 (1)顶点v入队列. (2)当队列非空时则继续执行,否则算法结束. (3)出队列取得队头顶点v: (4)查找顶点v的所 ...
- 手写一个最迷你的Web服务器
今天我们就仿照Tomcat服务器来手写一个最简单最迷你版的web服务器,仅供学习交流. 1. 在你windows系统盘的F盘下,创建一个文件夹webroot,用来存放前端代码. 2. 代码介绍: ( ...
- Python pip下载过慢解决方案
pip是一个python的包安装与管理工具,安装python时候可以选择是否安装,如果安装了pip可以使用命令查看版本 C:\Users\Vincente λ pip -V pip 19.2.3 fr ...
- tp5 日志的用途以及简单使用
相信大家对日志这个词都很熟悉,那么日志通常是用来做什么的呢? 找错误和监控 正常来说,日志对维运的帮助是最大的,特别是服务器或者是程序出现错误的时候. 那么现在我们就来看看,tp框架的日志是怎么设置的 ...
- 关于Java集合框架,这篇讲的还算不错了,建议大家看看!
集合框架 为什么要用集合而非数组: 虽然数组是保存一组对象最有效的方式,但是数组具有固定尺寸,但在写程序时并不知道将需要多少个对象.而集合能够自动地调整自己的尺寸. 打印数组时,必须使用Arrays. ...