最近需要接触大型数据存储,正好操作系统课上讲到了位图法,考虑到以后可能会用到,所以将以前的题目翻出来晒一晒
先来讲一讲简单的位图法(位向量法)
位向量顾名思义就是用位来存储一个数,文中说存储N=10000000个数,每一位代表一个数。
我们可以定义一个int类型的数组int a[N],那么如果a[9]的值为1,则表明文件中存在一个值为9。
这样的话,我们就可以用一个数组来表示这么多数。我们又知道,一个int型的数有4个字节,也就是32位,那么我们可以用N/32个int型数来表示这N个数:
a[0]表示第1~32个数(0~31)
a[1]表示第33~64个数(32~63)
这样,每当输入一个数字i,我们应该先找到该数字在数组的第几个元素中,也就是a[?],然后再确定在这个元素的第几位中。
举个例子来说,比如输入35,那么35/32为1余3,则应该将a[1]的第4位置为1。
好,有了上面的概念,可以先来看看set函数是怎么实现的:
void set(int i)
{
a[i>>SHIFT] |= (1<<(i &MASK));
}
除运算我们可以用右移来替代:
m>>n,表示m往右移动n位
输入i,除以32相当于往右移动5位,则i>>SHIFT代表i/32得到应该放在数组的第几个元素中,然后要置相应的位置位1了:
先来看看1<<(i&MASK)是什么意思。i&MASK相当于取i右移掉的部分,说白了就是取余数。
比如35的二进制表示是:… 0010 0011
MASK的二进制是0001 1111
两个相与操作得到0 0011
而右移5位,移掉的数是0 0011,换算成10进制是3,正是余数,与上面的操作值相等,都是0 0011。
因此1<<(i&MASK)就变成了1<<3,也就是将1右移3位,变成了1000。
然后在做一个|操作就将a[1]的第4位置1了。
联系之前,先来进阶一下位的表示:
2的幂 | 准确值X | 近似值 | X字节转换成MB、GB |
7 | 128 | ||
---|---|---|---|
8 | 256 | ||
10 | 1024 | 一千 | 1K |
16 | 65536 | 64K | |
20 | 1048576 | 一百万 | 1MB |
30 | 1073741824 | 十亿 | 1GB |
32 | 4294967296 | 4GB | |
40 | 1099511627776 | 一万亿 | 1TB |
题目:给定一个数组,包含1到N的整数,N最大位32000,数组可能包含重复的值,且N的取值不定,若只有4KB内存可用,该如何打印数组中可能重复的元素
解法
我们现在最多可以有4*2¹⁰ *8个比特,注意32*2¹⁰>32000,所以可以轻松的创建含有32000个比特的位向量,其中每个比特表示一个整数位。
部分代码(含注释)
public static void checkDuplicates(int[]array){
BitSet bs=new BitSet(32000+1);
for(int i=1;i<=array.length;i++){
int num=array[i];
if(bs.get(num))
System.out.println(num);
else
bs.set(num);
}
}
class BitSet{
int []bitset;
public BitSet(int size){
bitset=new int[size>>5]; //除以32,分多少组
}
boolean get(int pos){
int wordN=(pos>>5); //确定自己的组号
int bitN=(pos&0x1F);//得到除以32后的余数
return (bitset[wordN]&(1<<bitN))!=0; //如果存在则返回1,不存在返回0
}
void set(int pos){
int wordN=(pos>>5);
int bitN=(pos&0x1F);
bitset[wordN]|=1<<bitN; //将这位置为1
}
}
题目:给定一个输入文件,包含40亿个非负整数,请设计一种算法,产生一个不在该文件中的整数,假定有1GB内存来完成这项工作,
进阶:假如只有10MB内存可用,怎么办呢
解法:由题意可知读入使用字符流读入,现在有40亿个非负整数,1GB有将近十亿*8的比特,足够了。有些地方采用Byte,这里采用bit,解法同上,不赘叙。
进阶解法:
假如我现在有100个数,每次只能处理10个数,那怎么在100种找出没有出现的数呢。既然每次只能处理10个数,我将100分为10个区段。
第一个区段的数分别是1到10。我们将所有的数进行遍历,用一个count对这个区段出现的数进行累计,怎么累计呢?如果(i/10==1),那count++;
同理第二区段、第三区段。
那我们就可以用一个数组来表示所有的区段了,并对每个区段出现的数进行统计blocks[i/count]++;
这样我们通过记录,就可以找出缺数的区段。然后对这个区段运用上面的解法,记录然后遍历,就可以求解了。10MB=10*2²⁰B~2²³B————2²¹个整数
所以划分为每块可以存储2²¹个整数的区块
区块的个数:=2²³/2²¹——2000块
稍微调节一下,区块大小2²⁰,块数2¹²,
(1)首先扫描整个文件
如果属于区间[0,2²⁰-1],区块1++;如果属于[2²⁰,2²¹-1],区块2++…(2)各个区块,看各个区块的值是多少
如果值<2²⁰,此区块一定少元素(3)在少数字的区块通过前述位向量的方法计算,此处解法同上,不赘述。
感谢http://www.cnblogs.com/naonaoling/p/5251321.html
http://m.blog.csdn.net/fumier/article/details/46673529
http://www.cnblogs.com/marsdu/p/3181734.html
小结
事实再一次证明学好操作系统是一件多么重要的事情,作为技术人员就得以技术谋生,学计算机的当然就得明白计算机的相关知识,这次位图法确实很不错,希望以后还能够遇到吧