位图排序 大数据_位图排序

应用位图的排序

引言

生活中我们不可避免的会遇到大数据排序问题,这里是对《编程珠玑》中第1章中所描述的一类排序需求的简单手记。

背景

类似对存储7位电话号码的数据库进行整理,即对不超过一千万个不重复数据进行排序,限制内存占用1M和时间占用小于10s。

预备

时间

对于一趟循环即时间复杂度为O(n)的程序需要至少一千万此操作,嵌套一层循环则为O(n^2),即一千万的平方次,显然是不现实的。所以最佳方案是一趟排序即完成。

内存

在磁盘中排序显然没有全部读入内存中快速,所以要考虑的是如何在1M内存入最多的数据。Jon Bentley给出的思路令我非常惊奇,利用位图存储数据,即用一位数来表示一个7位数据。

位图表示数据

用一个20位长的字符串来表示一个所有元素都小于20的非负整数集,例如可用如下字符串来表示集合{1,2,3,5,8,13}

0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0

代表集合中数据的位置置1,其余位置置0。

思路

用一个10000000位数组对从0000000~9999999的不超过一千万个数进行按位存储,顺序输出即为排序后的数据。

伪代码

define m 10000000

//第一步,置零

for i = [0,m]

bit[i] = 0

//第二步,置数

for each i in the input file

bit[i] = 1

//第三步,取数

for i = [0,m]

if (bit[i] == 1)

print i

实践

随机生成一千万个数

Random random = new Random();

for (int i = 0; i < bit; i++) {

arr[i] = -1;

arr[i] = random.nextInt(bit-1);

mSort[i] = false;

}

排序

for(int i = 0; i< bit; i++){

if(arr[i] != -1)

mSort[arr[i]] = true;

}

输出

for(int i=0 ; i<99;i++){

if(mSort[i])

System.out.println(i);

}

完整实例

package bitmapsort;

import java.util.Random;

public class BitMap {

final int bit = 10000000;

private int[] arr = new int[bit];

private boolean[] mSort = new boolean[bit];

public BitMap() {

// TODO Auto-generated constructor stub

Random random = new Random();

for (int i = 0; i < bit; i++) {

arr[i] = -1;

arr[i] = random.nextInt(bit-1);

mSort[i] = false;

}

}

public void sort() {

for(int i = 0; i< bit; i++){

if(arr[i] != -1)

mSort[arr[i]] = true;

}

}

public void print(){

for(int i = 0 ; i< bit ;i++){

if(mSort[i])

System.out.println(i);

}

}

public static void main(String[] args) {

long time ;

time = System.currentTimeMillis();

BitMap BM = new BitMap();

time = System.currentTimeMillis() - time;

System.out.println("create time :"+time+"ms");

//位图排序

time = System.currentTimeMillis();

BM.sort();

time = System.currentTimeMillis() - time;

System.out.println("bit sort time :"+time+"ms");

//BM.print();

}

}

运行

第一次运行

第二次运行

第三次运行

第四次运行

此时可看到,使用该算法排序不超过一千万个7位整数仅需不到80ms左右,非常惊艳。

分析

此算法的排序速度虽然很快,但是也有一个弊端,就是这一千万个数中不能出现相同数据,不然会出现数据丢失。适用范围有限。

那么如果改进一下呢?

改进

version 1.1

假设同一个数会存在至多128个,则可以使用byte[]。

代码修改略

实际测试中时间依然为70ms左右。

version 1.2

既然时间充足,如果取消内存限制的话,可以尝试更换为int[],java中int的范围为“-232”到“232-1”,即“-2147483648”到“2147483647”。

此时足够处理一千个数都为同一个数的极限情况。

修改后的代码

public class BitMap {

final int bit = 10000000;

private int[] arr = new int[bit];

private int[] mSort = new int[bit];

public BitMap() {

// TODO Auto-generated constructor stub

Random random = new Random();

for (int i = 0; i < bit; i++) {

arr[i] = -1;

arr[i] = random.nextInt(bit-1);

mSort[i] = 0;

}

}

public void sort() {

for(int i = 0; i< bit; i++){

if(arr[i] != -1)

mSort[arr[i]] ++;

}

}

public void print(){

for(int i = 0 ; i < bit ;i++){

if(mSort[i] > 0)

System.out.println(i+"*"+mSort[i]);

}

}

测试

可见排序时间暴涨了一倍多,但还是在可接受范围内。

version 2.1

既然用到了interger,而interger又有2147483647这么大的范围,我们可以尝试将排位范围也提高到200000000即不超过2亿个9位整数(为什么不用20亿?因为java会提示堆栈内存超限)。

测试

此时时间达到了4s左右,但是对比我们排序的2亿个数,成绩应该很可喜了。

分段输出

由于如此庞大的数据除了导出到文本文件里,几乎不可能实时查看,所以我们对某一区间进行测试输出。

0~50

1234567~1234600

间接反映了随机数生成方法还是比较稳定的。。。(笑)

总结

除了对Jon Bentley的佩服还是佩服。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值