[珠玑拾遗]之一------通俗易懂解读位向量和Java实现
来源:互联网 发布:淘宝包店名字 编辑:程序博客网 时间:2024/06/08 15:50
前序
昨夜晚归,兴之所至,翻阅旧书,《编程珠玑》,薄尘轻蒙,遂感慨无数,静心而读。忽遇难解之习题,问诸西洋必应者,得一文曰[珠玑之椟]位向量/位图的定义和应用,其思明而言简,不得其意,研习良久,终顿开茅塞,于今日作拙,欲通俗易懂见诸Java矣。
一、背景介绍
首先我们来看一下Bentley大师在《编程珠玑》第一章提出的问题:
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7。如果在输入文件中有任何整数重复出现就是致命错误(文件中的整数互异)。没有其他数据与该整数相关联。
输出:按升序排列的输入整数的列表。
约束:最多有1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,最理想的时间是10S。
其中具体的分析过程不再重复,感兴趣的朋友可以查看原书,下面直接列出大师的解决方案。
数据结构:位向量(亦作位图,不过与图形学中的位图混淆,下作位向量)。一个n位的二进制数据,数据i如果出现在该二进制的第i位,则该位置为1,否则为0。如:用一个10位长的二进制数据表示元素都小于10的集合,{1,2,3,5,8},该集合用二进制数据的表现形式:01110100100
算法分析:分三步解决
1. 初始化集合,每个位都置为0;
2. 读入文件的每个整数,将对应的位置为1;
3. 遍历二进制数据,如果该位为1,则输出相应的整数。伪代码:
/*第一步:遍历二进制数组,都置为0,进行初始化for i = [0,n) bit[i] = 0/*第二步:读取文件,将整数对于的位置为1for each i in the file bit[i] = 1;/*第三步:将已排序的整数遍历输出for i = [0,n) if bit[i] == 1 print(i)
好,以上我们将位向量数据结构的概念了解清楚了,下面遇到一个书上的习题。
二、提出问题
2.如何使用位逻辑运算(如与、或、移位)来实现位向量?
第一眼看到这个题目时我很纳闷儿,不是可以直接操作二进制位的数组吗,为什么还要使用位逻辑运算来实现位向量呢。
然后转念一想,像Java/C++这类高级语言是没有bit这种数据类型的,占位最小的数据类型就是byte,一个字节,八位。
所以我们如果要用Java实现位向量的话,需要考虑如何运用位运算了。
三、解决问题
重新定义问题:假设有一组互异且小于N的正整数,需要使用位向量来进行升序排列,请用Java实现。
问题解析:
- 首先我们要明确对于位向量来说,要存储一组小于N的正整数,那么就需要N位的二进制数。
- 而如果使用int数组来表示二进制位,1int=4byte=4*8bit,那么一个int元素就可以表示32位,即存储小于32的正整数集合,两个int元素可以表示64位,即存储小于64的正整数集合。
- 那么问题来了,如果要表示小于N的正整数集合,需要多少个int元素?
array.length = (N-1)/32 + 1 - 如何定位正整数i在数组中的位置呢? 一句话:32整除i,商Q是int数组的下标,余数R是1在这个int元素中的位数。
我们可以想象一下,i用位向量表示就是000000….1….,其中1所在的位置是i。可以想象成这个很长的位向量对齐int数组的首位,然后倒向int数组(数组按照一个二进制位一个槽来表示) - 目前我们已经定位了i的位置,下一步考虑如何进行三个很重要的操作 1.置位;2.置零,3.读取
- 置位:用余数(二进制表示,即1 << Q)与相应的int元素做或操作
- 置零:将余数(二进制表示,即1 << Q)取反,然后结果与int元素做与运算
- 读取:用余数与相应的int元素做与运算,得到int元素中该位置的值,如为1则返回1,为0则返回0。
实现:
package com.rambo.P1;import java.util.ArrayList;import java.util.List;public class BitVetory { private int n; private int[] bitArray; private static final int BIT_LENGTH = 32;//默认使用int类型 private static int P; private static int Q; /** * 初始化位向量 * @param n */ public BitVetory(int n) { this.n = n; bitArray = new int[(n-1)/BIT_LENGTH + 1]; init(); } /** * 初始化操作 */ public void init(){ for (int i = 0; i < n; i++) { clr(i); } } /** * 获取排序后的数组 * @return */ public List<Integer> getSortedArray(){ List<Integer> sortedArray = new ArrayList<>(); for (int i = 0; i < n; i++) { if (get(i) == 1) { sortedArray.add(i); } } return sortedArray; } /** * 置位操作 * @param i */ public void set(int i){ P = i / BIT_LENGTH; Q = i % BIT_LENGTH; bitArray[P] |= 1 << Q; } /** * 置零操作 * @param i */ public void clr(int i){ P = i / BIT_LENGTH; Q = i % BIT_LENGTH; bitArray[P] &= ~(1 << Q); } /** * 读取操作 * @param i * @return */ public int get(int i){ P = i / BIT_LENGTH; Q = i % BIT_LENGTH; return Integer.bitCount(bitArray[P] & (1 << Q)); }}package com.rambo.P1.test;import java.util.ArrayList;import java.util.List;import java.util.Random;import com.rambo.P1.BitVetory;public class TestMain { public static void main(String[] args) { int amount = 15; List<Integer> randoms = getRandoms(amount); System.out.println("排序前数组:"); BitVetory bitVetory = new BitVetory(amount); for (Integer e : randoms) { System.out.print(e+","); bitVetory.set(e); } List<Integer> sortedArray = bitVetory.getSortedArray(); System.out.println(); System.out.println("排序后数组:"); for (Integer e : sortedArray) { System.out.print(e+","); } } private static List<Integer> getRandoms(int amount) { Random random = new Random(); List<Integer> randoms = new ArrayList<>(); while(randoms.size() < (amount - 1)){ int element = random.nextInt(amount - 1) + 1;//element ∈ [1,amount) if (!randoms.contains(element)) { randoms.add(element); } } return randoms; }}
输出:
排序前数组:
11,7,12,1,2,9,3,13,6,5,14,4,8,10
排序后数组:
1,2,3,4,5,6,7,8,9,10,11,12,13,14
好,如此这般我们便使用Java语言实现了位向量。
问题?
我们的代码是否还有可优化的空间呢?从位操作符的角度考虑。
.
.
.
.
.
.
..
.
.
.
.
.
.
.
.
.
.
.
.
.
将P = i / BIT_LENGTH改成P = i >> 5(2^5=32)
将Q = i % BIT_LENGTH改成Q = i & 0x1F (0x1F = 11111)
- [珠玑拾遗]之一------通俗易懂解读位向量和Java实现
- 编程珠玑位逻辑运算实现位向量
- 【编程珠玑】自己实现位向量类
- 编程珠玑之1.2位逻辑运算实现位向量
- 《编程珠玑》习题-如何用位逻辑实现位向量
- 编程珠玑:通过位逻辑运算实现位向量
- [珠玑之椟]位向量/位图的定义和应用
- 学习《编程珠玑》-位向量/位图的定义和应用
- 《编程珠玑》读书笔记1----------------如何使用位逻辑来实现位向量
- 编程珠玑--如何使用位逻辑运算(例如不、或、移位)来实现位向量
- 编程珠玑 如何使用位逻辑运算(例如与、或、移位)来实现位向量
- <<编程珠玑>>笔记之使用位逻辑运算来实现位向量
- 《编程珠玑》(第二版)第一章习题2(用位运算实现位向量)
- java学习拾遗之一
- 【编程珠玑】排序与位向量
- 【编程珠玑——01】位向量
- [编程珠玑]-第一章:位图/位向量排序
- 【编程珠玑】实现位数组
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)
- iOS下单签名串之字典通过key值进行降序排列
- nginx rewrite 指令
- 最小木棒问题 dfs+ 减枝
- Java编写文件拷贝
- [珠玑拾遗]之一------通俗易懂解读位向量和Java实现
- CV1——二值图像分析之连通域
- RMAN的一些脚本
- 大数据系列修炼-Scala课程45
- caffe学习笔记
- 实现SLG地图
- 使用Glide下载图片
- bzoj 3757 苹果树(树上莫队)
- 栈(括号匹配问题)