问题引入:

1.给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
2.给定一个千万级别数据量的整数集合,判断哪些是重复元素。
3.给定一个千万级别数据量的整形数组,对其进行排序。
4.在5亿个整数中找出不重复的整数(注意,假设内存不足以容纳这5亿个整数)。

从数据量上看,使用常规的解法(普通排序算法,逐个比较等)明显不合适,所以这里我们引入一个新的解法,就是Bitmap。

Bitmap就是用一个bit位来标记某个元素对应的Value, 而Key即是该bit的位序。由于采用了Bit为单位来存储数据,因此可以大大节省存储空间。 bitmap通过1个位表示一个状态,比如:int类型有2^32个数字,即4G个数字,那么每个数字一个状态,就是2^32个bit,即512 MB(也就是说,用512兆存储空间就可以处理4G个数据,即40+亿数据)。

下面是我用C++写的一个bitmap类,可以通过构造对象时传入数据规模,动态申请所需的内存,然后处理用户的大量数据:

 #include<iostream>
#include<fstream>
#include<ctime>
using namespace std;
const unsigned SIZE = ;//512兆静态存储区可处理40.96亿数据 class Bitmap {
typedef struct Byte {
unsigned char bit8;
static const unsigned char mask[];//用来取得一个字节每一位的辅助数组
Byte()
{
bit8 = ;
}
//设置该位,就是存储该数
void set1(unsigned at)
{
bit8 |= mask[at];
}
//读取该位是否有数
bool get1(unsigned at)
{
return bit8 & mask[at];
}
} Byte;
Byte *m_byte;
unsigned m_size;
public:
Bitmap(unsigned _size)
{
m_byte = new Byte[(_size+)/];
m_size = _size;
}
virtual ~Bitmap()
{
delete[] m_byte;
m_size = ;
}
//存储一个数据
bool push(unsigned data)
{
if(data>=m_size)
return false;
m_byte[data/].set1(data%);
return true;
}
//读取一个数据是否存在
bool find(unsigned data)
{
return data>=m_size ? : m_byte[data/].get1(data%);
}
//返回能存储的数据个数
unsigned size()
{
return m_size;
}
//重载运算符实现常用功能
//存储一个数据
bool operator>>(unsigned data)
{
return push(data);
}
//读取一个数据是否存在
bool operator<<(unsigned data)
{
return find(data);
}
//访问到某个数据块
Byte& operator[](unsigned i)
{
if(i>=m_size/)
throw "index out of range";
return m_byte[i];
}
};
const unsigned char Bitmap::Byte::mask[] = {0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1};//用来取得一个字节每一位的辅助数组 int main()
{
Bitmap bitmap(*SIZE);//可以存储40+亿数据
ifstream file("in.txt");//从文件内录入一些整数
unsigned read, i=, t1 = clock();
for(i=; i<SIZE; ++i)
if(file>>read)
bitmap>>read;
else
break;
file.close();
cout<<"共存储"<<i/<<"W 数据, "<<"耗时: "<<clock()-t1<<"ms"<<endl;
t1 = clock();
for(i=; i<; ++i)
if(bitmap<<i)
;
cout<<"访问"<<i/<<"W 数据共耗时: "<<clock()-t1<<"ms"<<endl;
cout<<"请输入需要检索的数据:"<<endl;
while(cin>>read) {
if(bitmap<<read)
cout<<"已存储"<<read<<endl;
else
cout<<"Error: 未存储"<<read<<endl;
}
return ;
}

运行结果如下:

在程序中,先读取一个文本文件中随机产生的6W个整数,存到这个bitmap中,然后测试了一下从这个建立好的bitmap中查找100W数据耗时情况(11ms左右),接下来的部分是用户可以手动输入一些整数,程序会自动检索bitmap中是否已存储该数据。

这样就可以解决引入话题中的第一个问题了,把输入的文本数据改为已知的40亿数据就可以了(40亿数据的录入可能需要一会儿,大概1300秒)。

下面给出引入的剩余三个问题的解题思路。

问题2:先建立一个足够大的Bitmap对象,然后依次录入这些数据,如果录入某数据前发现该位已经为1,则该数据重复,依次得到重复的数据即可。

问题3:先建立一个足够大的Bitmap对象,然后依次录入这些数据,从Bitmap开始位置起遍历,如果某位不为0,则表示有该数据,依次输出不为0的位的位序就是排序好的数组(输出太多没意义,可以将输出转换到写入文件,那么新文件中数据就是排序好的)。

问题4方法1,建立2个足够大的Bitmap对象,依次录入数据,录入前先判断该数据在bitmap1中是否存在(即对应位是否为1),不存在则录入到bitmap1中,存在就录入到bitmap2中;全部录入完后,依次遍历bitmap1中每一位,如果某一位为1但是bitmap2中对应位不为1,则表示该数据只出现过一次,依次输出即可。

方法2,建立一个足够大的Bitmap对象,不过用两位表示一个数据,00表示数据不存在,01数据出现一次,10表示数据出现多次。11呢?一边凉快去吧,不要你了,哈哈。这样依次录入数据时,如果该对应位(其实是两位)为00则改为01,01就改为10,10就不管了。录入完成后,遍历整个bitmap,找到01位就输出。

——————————————————————————————我是分割线—————————————————————————————

  好了,常见的大数据题目就通过bitmap这个神奇的结构给解决了,不过bitmap也不是万能的,很明显,它暂时只适合存储整形数据,当然这里只考虑了unsigned类型数据,如果是int类型的话,对应映射一下就可以了也是没问题的。不过即使如此,也只能处理10亿级别的数据,如果数据量更大、类型不只是整形呢?

  比如:需要写一个网络蜘蛛(web crawler)。由于网络间的链接错综复杂,蜘蛛在网络间爬行很可能会形成“环”。为了避免形成“环”,就需要知道蜘蛛已经访问过哪那些URL。给一个URL,怎样知道蜘蛛是否已经访问过?

  不难想到如下几种方案:

  1. 将访问过的URL全部保存到数据库;

  2. 用HashSet将访问过的URL保存起来。那只需接近O(1)的代价就可以查到一个URL是否被访问过;

  3. URL经过MD5或SHA-1等单向哈希后再保存到HashSet或数据库。

  4. Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位。

  方法1~3都是将访问过的URL完整保存,方法4则只标记URL的一个映射位。

  以上方法在数据量较小的情况下都能完美解决问题,但是当数据量变得非常庞大时问题就来了。

  方法1:数据量变得非常庞大后关系型数据库查询的效率会变得很低。而且每来一个URL就启动一次数据库查询是不是太小题大做了?

  方法2:太消耗内存。随着URL的增多,占用的内存会越来越多。就算只有1亿个URL,每个URL只算50个字符,就需要5GB内存。

  方法3:由于字符串经过MD5处理后的信息摘要长度只有128Bit,SHA-1处理后也只有160Bit,因此方法3比方法2节省了好几倍的内存。

  方法4:消耗内存是相对较少的,但缺点是单一哈希函数发生冲突的概率太高。还记得数据结构课上学过的Hash表冲突的各种解决方法么?若要降低冲突发生的概率到1%,就要将BitSet的长度设置为URL个数的100倍。

  但是我们可以考虑如果在一定程度上忽略误判的情况,那么是不是可以通过改进方法4实现这一功能?其实这就是Bloom Filter的算法 的思想:Bloom Filter是由Bloom在1970年提出的一种多哈希函数映射的快速查找算法。通常应用在一些需要快速判断某个元素是否属于集合,但是并不严格要求100%正确的场合。其思想就是在方法4基础上做了一些改进,不是映射到一位,而是通过K个哈希函数映射到K位上,这样只有当新的URL计算得到的K位都为1时才判断为该URL已经访问过(有误判的可能性,不过有相关研究证明,取得合适的K值和bitmap位数时可以让误判率很小以至于可以忽略,参见细节

  当然,还可以通过map-reduce来处理,毕竟人家mapreduce可是行家,专业的大数据处理技术嘛!

  参考文献:

  Bitmap位图法

  BloomFilter——大规模数据处理利器

  BloomFilter概念及原理 

  欢迎浏览我的博客,小生初来乍到,哪里有问题请多多指教,转载请注明出处!http://www.cnblogs.com/webary/p/4733247.html

Bit-map法处理大数据问题的更多相关文章

  1. 【大数据技巧】日均2TB日志数据在线快速处理之法

    [大数据技巧]日均2TB日志数据在线快速处理之法 http://click.aliyun.com/m/8958/

  2. 大数据:Map终结和Spill文件合并

    当Mapper没有数据输入,mapper.run中的while循环会调用context.nextKeyValue就返回false,于是便返回到runNewMapper中,在这里程序会关闭输入通道和输出 ...

  3. 【大数据】深入源码解析Map Reduce的架构

    这几天学习了MapReduce,我参照资料,自己又画了两张MapReduce的架构图. 这里我根据架构图以及对应的源码,来解释一次分布式MapReduce的计算到底是怎么工作的. ​话不多说,开始! ...

  4. php 大数据量及海量数据处理算法总结

    下面的方法是我对海量数据的处理方法进行了一个一般性的总结,当然这些方法可能并不能完全覆盖所有的问题,但是这样的一些方法也基本可以处理绝大多数遇到的问题.下面的一些问题基本直接来源于公司的面试笔试题目, ...

  5. 天池大数据周冠军分享|附移动推荐算法赛答辩会Top5选手PPT

    上周是淘宝穿衣搭配算法大赛开始评测后的第一周,周冠军是来自浙江大学的"FUC AUTH"队.他们在夺得本周冠军之后,还将自己的获胜经验分享给了大家,究竟有什么秘诀呢? 阿里巴巴天池 ...

  6. 大数据和Hadoop生态圈

    大数据和Hadoop生态圈 一.前言: 非常感谢Hadoop专业解决方案群:313702010,兄弟们的大力支持,在此说一声辛苦了,经过两周的努力,已经有啦初步的成果,目前第1章 大数据和Hadoop ...

  7. Spark大数据针对性问题。

    1.海量日志数据,提取出某日访问百度次数最多的那个IP. 解决方案:首先是将这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中.注意到IP是32位的,最多有个2^32个IP.同样可以采 ...

  8. java面试(2)--大数据相关

    第一部分.十道海量数据处理面试题 1.海量日志数据,提取出某日访问百度次数最多的那个IP. 首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中.注意到IP是32位的,最多有个2^ ...

  9. 【大数据】Scala学习笔记

    第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java  以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...

随机推荐

  1. Linux python2.4升级到2.7

    yum install gcc-c++  gcc -y mkdir /usr/local/python2.7tar zxvf Python-2.7.11.tgzcd Python-2.7.11/./c ...

  2. Android4.4 ContentResolver查询图片无效 及 图库删除 添加图片后,ContentResolver不更新的问题解决

    问题背景: 參考链接 做了一个图片浏览,用ContentResolver扫描图库照片.且严格依照时间拍摄顺序排好序显示在listview里.例如以下图所看到的: watermark/2/text/aH ...

  3. python:类与对象命名空间、面对对象的组合用法

    1,类里可以定义两种属性: #静态属性 #静态属性就是直接在类中定义的变量 #动态属性 #动态属性就是定义在类中的方法 class Course: language = ['Chinese']#静态属 ...

  4. POJ Treasure Exploration 【DAG交叉最小路径覆盖】

    传送门:http://poj.org/problem?id=2594 Treasure Exploration Time Limit: 6000MS   Memory Limit: 65536K To ...

  5. POJ Widget Factory 【求解模线性方程】

    传送门:http://poj.org/problem?id=2947 Widget Factory Time Limit: 7000MS   Memory Limit: 65536K Total Su ...

  6. 程序的优化(PHP)

    有些小细节往往容易被人忽视.有时候常常说优化代码优化代码,但是实际操作的时候,最容易被忽视的如下所示: echo 比 print 快. 使用echo的多重参数代替字符串连接. 在执行for循环之前确定 ...

  7. FreeImage.lib库的配置和简单使用 转

    转载地址 http://www.codeweblog.com/win8-1%E4%B8%8Bfreeimage-lib%E5%BA%93%E7%9A%84%E9%85%8D%E7%BD%AE%E5%9 ...

  8. 在VS2010 中使用subversion 进行代码的分支与合并

    在实际开发总,遇到了这种情况: 开发版本1,开发版本2 ,更新产品时要求1在前,2在后. 但是因为时间要求,必须2个版本同时开发.这时就想到了在svn的版本分支合并. 创建分支之前,首先把当前版本代码 ...

  9. 关于Echarts的原生js获取DOM元素与动态加载DOM元素的冲突问题

    1.前言: 最近在做的看板项目,因为需要循环加载后台数据,并且用Echarts做数据呈现,所以jQuery和angular等库统统靠边站,Echarts用的是原生js获取DOM元素,至于诸多不兼容等深 ...

  10. Java面向对象知道这些就够了

    面向对象 面向对象是一种思维方式,相对于面向过程而言的. 面向过程在流程中关注动作执行的每一个细节 — 自己动手做 面向对象重点找这个对象,只要找到了对象,那么这个对象所具有的功能就能够被使用 — 找 ...