利用位向量实现海量数据存储

最近需要接触大型数据存储,正好操作系统课上讲到了位图法,考虑到以后可能会用到,所以将以前的题目翻出来晒一晒

先来讲一讲简单的位图法(位向量法)

位向量顾名思义就是用位来存储一个数,文中说存储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
7128
8256
101024一千1K
166553664K
201048576一百万1MB
301073741824十亿1GB
3242949672964GB
401099511627776一万亿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

小结
事实再一次证明学好操作系统是一件多么重要的事情,作为技术人员就得以技术谋生,学计算机的当然就得明白计算机的相关知识,这次位图法确实很不错,希望以后还能够遇到吧

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值