zoukankan      html  css  js  c++  java
  • 利用BitMap进行大数据排序去重

    1、问题

    问题提出

    M(如10亿)个int整数,只有其中N个数重复出现过,读取到内存中并将重复的整数删除。

    2、解决方案

    问题分析

    我们肯定会先想到在计算机内存中开辟M个int整型数据数组,来one bye one读取M个int类型数组, 然后在一一比对数值,最后将重复数据的去掉。当然这在处理小规模数据是可行的。

    我们考虑大数据的情况:例如在java语言下,对10亿个int类型数据排重。

    java中一个int类型在内存中占4byte。那么10亿个int类型数据共需要开辟10^9次方*4byte≈4GB的连续内存空间。以32位操作系统电脑为例,最大支持内存为4G,可用内存更是小于4G。所以上述方法在处理大数据时根本行不通。

    思维转化

    既然我们不能为所有 int 类型的数据开辟 int 类型数组,那么可以采取更小的数据类型来读取缓存 int 类型数据。考虑到计算机内部处理的数据都是 01 序列的bit,那么我们是否可以用 1bit 来表示一个 int 类型数据。

    位映射的引出

    使用较小的数据类型指代较大的数据类型。如上所说的问题,我们可以用1个 bit 来对应一个int 整数。假如对应的 int 类型的数据存在,就将其对应的 bit 赋值为1,否则,赋值为0(boolean类型)。java中 int 范围为 -2^31 到 2^31-1. 那么所有可能的数值组成的长度为2^32. 对应的 bit 长度也为 2^32. 那么可以用这样处理之后只需要开辟2^32 bit = 2^29 byte = 512M 大小的 内存空间 。显然,这样处理就能满足要求了,虽然对内存的消耗也不太小。

    问题解决方案

    首先定义如下图的int - byte 映射关系,当然,映射关系可以自定义。但前提要保证你的数组上下标不能越界。

    但如上定义的bit[]数组显然在计算机中是不存在的,所我们需要将其转化为 java 中的一个基本数据类型存储。显然,byte[] 是最好的选择。

    将其转化为byte[] 数组方案:

    自定义的映射关系表,每个bit对应一个 int 数值,将 int 的最大值,最小值与数组的最大最小索引相对应。从上图可以看出来 int 数值与bit索引相差 2^31次方。当然,你也可以定义其他的映射关系,只是注意不要发生数组越界的情况。

    bit[]索引:由于最大值可能是2^32,故用long接收: long bitIndex = num + (1l << 31); 

    byte[]索引:  int index = (int) (bitIndex / 8);  ,在字节byte[index]中的具体位置:  int innerIndex = (int) (bitIndex % 8); 

    更新值: dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex)); 

    3、代码

     1 import java.util.Random;
     2 
     3 /**
     4  * 问题:M(如10亿)个int整数,只有其中N个数重复出现过,读取到内存中并将重复的整数删除。<br/>
     5  * 使用位映射来进行海量数据的去重排序,原先一个元素用一个int现在只用一个bit, 内存占比4*8bit:1bit=32:1<br/>
     6  * 亦可用java语言提供的BitSet,不过其指定bit index的参数为int类型,因此在此例中将输入数转为bit index时对于较大的数会越界<br><br/>
     7  */
     8 public class BigDataSort {
     9 
    10     private static final int CAPACITY = 1_000_000;// 数据容量
    11 
    12     public static void main(String[] args) {
    13 
    14         testMyFullBitMap();
    15 
    16     }
    17 
    18     public static void testMyFullBitMap() {
    19         MyFullBitMap ms = new MyFullBitMap();
    20 
    21         byte[] bytes = null;
    22 
    23         Random random = new Random();
    24         long startTime = System.currentTimeMillis();
    25         for (int i = 0; i < CAPACITY; i++) {
    26             int num = random.nextInt();
    27             // System.out.println("读取了第 " + (i + 1) + "	个数: " + num);
    28             bytes = ms.setBit(num);
    29         }
    30         long endTime = System.currentTimeMillis();
    31         System.out.printf("存入%d个数,用时%dms
    ", CAPACITY, endTime - startTime);
    32 
    33         startTime = System.currentTimeMillis();
    34         ms.output(bytes);
    35         endTime = System.currentTimeMillis();
    36         System.out.printf("取出%d个数,用时%dms
    ", CAPACITY, endTime - startTime);
    37     }
    38 }
    39 
    40 class MyFullBitMap {
    41     // 定义一个byte数组表示所有的int数据,一bit对应一个,共2^32b=2^29B=512MB
    42     private byte[] dataBytes = new byte[1 << 29];
    43 
    44     /**
    45      * 读取数据,并将对应数数据的 到对应的bit中,并返回byte数组
    46      * 
    47      * @param num
    48      *            读取的数据
    49      * @return byte数组 dataBytes
    50      */
    51     public byte[] setBit(int num) {
    52 
    53         long bitIndex = num + (1l << 31); // 获取num数据对应bit数组(虚拟)的索引
    54         int index = (int) (bitIndex / 8); // bit数组(虚拟)在byte数组中的索引
    55         int innerIndex = (int) (bitIndex % 8); // bitIndex 在byte[]数组索引index 中的具体位置
    56 
    57         // System.out.println("byte[" + index + "] 中的索引:" + innerIndex);
    58 
    59         dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));
    60         return dataBytes;
    61     }
    62 
    63     /**
    64      * 输出数组中的数据
    65      * 
    66      * @param bytes
    67      *            byte数组
    68      */
    69     public void output(byte[] bytes) {
    70         int count = 0;
    71         for (int i = 0; i < bytes.length; i++) {
    72             for (int j = 0; j < 8; j++) {
    73                 if (((bytes[i]) & (1 << j)) != 0) {
    74                     count++;
    75                     int number = (int) ((((long) i * 8 + j) - (1l << 31)));
    76                     // System.out.println("取出的第 " + count + "	个数: " + number);
    77                 }
    78             }
    79         }
    80     }
    81 }
    View Code

    4、参考资料

    http://yacare.iteye.com/blog/1969931

  • 相关阅读:
    最短路变形题目 HDU多校7
    交换一个数字的任意两个位置,指定K次的最值
    七彩线段
    带限制的广搜 codeforces
    在一个矩阵内求一个最长上升子序列
    函数,以及三元运算符
    文件操作
    基础数据类型补充以及python编码
    深浅copy,is和==区别,集合,列表的操作
    字典和枚举
  • 原文地址:https://www.cnblogs.com/z-sm/p/6238977.html
Copyright © 2011-2022 走看看