Bit-map法处理大数据问题

问题引入:

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类,可以通过构造对象时传入数据规模,动态申请所需的内存,然后处理用户的大量数据:

  1 #include<iostream>
  2 #include<fstream>
  3 #include<ctime>
  4 using namespace std;
  5 const unsigned SIZE = 512000000;//512兆静态存储区可处理40.96亿数据
  6
  7 class Bitmap {
  8     typedef struct Byte {
  9         unsigned char bit8;
 10         static const unsigned char mask[9];//用来取得一个字节每一位的辅助数组
 11         Byte()
 12         {
 13             bit8 = 0;
 14         }
 15         //设置该位,就是存储该数
 16         void set1(unsigned at)
 17         {
 18             bit8 |= mask[at];
 19         }
 20         //读取该位是否有数
 21         bool get1(unsigned at)
 22         {
 23             return bit8 & mask[at];
 24         }
 25     } Byte;
 26     Byte *m_byte;
 27     unsigned m_size;
 28 public:
 29     Bitmap(unsigned _size)
 30     {
 31         m_byte = new Byte[(_size+7)/8];
 32         m_size = _size;
 33     }
 34     virtual ~Bitmap()
 35     {
 36         delete[] m_byte;
 37         m_size = 0;
 38     }
 39     //存储一个数据
 40     bool push(unsigned data)
 41     {
 42         if(data>=m_size)
 43             return false;
 44         m_byte[data/8].set1(data%8);
 45         return true;
 46     }
 47     //读取一个数据是否存在
 48     bool find(unsigned data)
 49     {
 50         return data>=m_size ? 0 : m_byte[data/8].get1(data%8);
 51     }
 52     //返回能存储的数据个数
 53     unsigned size()
 54     {
 55         return m_size;
 56     }
 57     //重载运算符实现常用功能
 58     //存储一个数据
 59     bool operator>>(unsigned data)
 60     {
 61         return push(data);
 62     }
 63     //读取一个数据是否存在
 64     bool operator<<(unsigned data)
 65     {
 66         return find(data);
 67     }
 68     //访问到某个数据块
 69     Byte& operator[](unsigned i)
 70     {
 71         if(i>=m_size/8)
 72             throw "index out of range";
 73         return m_byte[i];
 74     }
 75 };
 76 const unsigned char Bitmap::Byte::mask[9] = {0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1};//用来取得一个字节每一位的辅助数组
 77
 78 int main()
 79 {
 80     Bitmap bitmap(8*SIZE);//可以存储40+亿数据
 81     ifstream file("in.txt");
 82     unsigned read, i=0, t1 = clock();
 83     for(i=0; i<SIZE; ++i)
 84         if(file>>read)
 85             bitmap>>read;
 86         else
 87             break;
 88     file.close();
 89     cout<<"共存储"<<i/10000<<"W 数据, "<<"耗时: "<<clock()-t1<<"ms"<<endl;
 90     t1 = clock();
 91     for(i=0; i<1000000; ++i)
 92         if(bitmap<<i)
 93             ;
 94     cout<<"访问"<<i/10000<<"W 数据共耗时: "<<clock()-t1<<"ms"<<endl;
 95     cout<<"请输入需要检索的数据:"<<endl;
 96     while(cin>>read) {
 97         if(bitmap<<read)
 98             cout<<"已存储"<<read<<endl;
 99         else
100             cout<<"Error: 未存储"<<read<<endl;
101     }
102     return 0;
103 }

运行结果如下:

在程序中,先读取一个文本文件中随机产生的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

时间: 2024-10-01 07:33:48

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

数据挖掘在大数据中的应用综述

*** (上海海事大学 上海 201306) 摘 要: 面对大规模多源异构的数据,数据挖掘的方法不断的得到改善与发展,同时对于数据挖掘体系的完善也提出了新的挑战.针对当前数据挖掘在大数据方面的应用,本文从数据挖掘的各个阶段进行了方法论的总结及应用,主要包括数据准备的方法.数据探索的方法.关联规则方法.数据回归方法.数据分类方法.数据聚类方法.数据预测方法和数据诊断方法.最后还指出类数据挖掘在鲁棒性表达方面的进一步研究. 关键词: 数据挖掘;方法论;大数据;鲁棒性 Application of D

硅谷观察之大数据篇【下】:硅谷巨头们的大数据玩法

在硅谷观察之大数据篇的[上]篇中,我把硅谷地区大数据生长状况基本梳理了一个相对完整的形状出来.有朋友看了[下]的预告后在微博上给我留言说,听说下篇要介绍一些公司的大数据部门情况,问能不能点名加个Google尤其是Google Maps,因为特别想知道这个世界上最大的搜索引擎以及每天必不可少的出行神器是怎么当一个挖掘机的. 于是,上周我又去了Google采访.本篇将一共呈现硅谷四大不同类型的公司如何玩转大数据,其中包括了著名FLAG中的三家(Apple在大数据这块来说表现并不突出). 本篇内容来自

台湾为何电商不发达,参考下他们超级发达便利店的“大数据”学习法

钛媒体注:一直以来电商圈都有一个问题,为何台湾的电商就怎么都发展不起来?这实在是可以"归罪于"他们太过于发达的超商门店,seven eleven 和全家便利店等开到到处都是,转角都能遇到爱.这也许是一方面,另一方面,是这些强大超商体系不断更新的技术商业知识体系,常有各种创意和最前沿的技术学习法.今天钛媒体电商研究院的电商课堂,就邀请<商业价值>编委刘梅来讲讲便利超市门店的"商业学习实验": 零售企业可能每天都会产生令人兴奋但有些风险的创意,这些点子要不要

王家林亲传《DT大数据梦工厂》第三讲Tuple、Array、Map与文件操作入门实战

你想了解大数据,你想成为年薪百万吗?那你还等着什么,快点来吧!跟着王家林老师学习spark大数据 第三讲Tuple.Array.Map与文件操作入门实战 Tuple object TupleOps  { def  main (args: Arrag[string]): Unit = { val triple = (100,”Scala”,”Spark”) printIn(triple._1) printIn(triple._2) } } Array object ArrayOperations

ng机器学习视频笔记(十五) ——大数据机器学习(随机梯度下降与map reduce)

ng机器学习视频笔记(十五) --大数据机器学习(随机梯度下降与map reduce) (转载请附上本文链接--linhxx) 一.概述 1.存在问题 当样本集非常大的时候,例如m=1亿,此时如果使用原来的梯度下降算法(也成为批量梯度下降算法(batch gradient descent),下同),则速度会非常慢,因为其每次遍历整个数据集,才完成1次的梯度下降的优化.即计算机执行1亿次的计算,仅仅完成1次的优化,因此速度非常慢. 2.数据量考虑 在使用全量数据,而不是摘取一部分数据来做机器学习,

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

当Mapper没有数据输入,mapper.run中的while循环会调用context.nextKeyValue就返回false,于是便返回到runNewMapper中,在这里程序会关闭输入通道和输出通道,这里关闭输出通道并没有关闭collector,必须要先flush一下. 获取更多大数据视频资料请加QQ群:947967114 代码结构: Maptask.runNewMapper->NewOutputCollector.close->MapOutputBuffer.flush 我们看flus

离线轻量级大数据平台Spark之MLib机器学习库概念学习

Mlib机器学习库 1.1机器学习概念 机器学习有很多定义,倾向于下面这个定义.机器学习是对能通过经验自动改进的计算机算法的研究.机器学习依赖数据经验并评估和优化算法所运行出的模型.机器学习算法尝试根据训练数据使得表示算法行为的数学目标最大化,并以此来进行预测或作出决定.机器学习问题分类为几种,包括分类.回归.聚类.所有的机器学习算法都经过一条流水线:提取训练数据的特征->基于特征向量训练模型->评估模型选择最佳.特征提取主要是提取训练数据中的数值特征,用于数学建模.机器学习一般有如下分类:

大数据量处理

1.100亿个数字找出最大的10个 1.首先一点,对于海量数据处理,思路基本上是确定的,必须分块处理,然后再合并起来. 2.对于每一块必须找出10个最大的数,因为第一块中10个最大数中的最小的,可能比第二块中10最大数中的最大的还要大. 3.分块处理,再合并.也就是Google MapReduce 的基本思想.Google有很多的服务器,每个服务器又有很多的CPU,因此,100亿个数分成100块,每个服务器处理一块,1亿个数分成100块,每个CPU处理一块.然后再从下往上合并.注意:分块的时候,

大数据技术大合集:Hadoop家族、Cloudera系列、spark、storm【转】

大数据我们都知道hadoop,可是还会各种各样的技术进入我们的视野:Spark,Storm,impala,让我们都反映不过来.为了能够更好 的架构大数据项目,这里整理一下,供技术人员,项目经理,架构师选择合适的技术,了解大数据各种技术之间的关系,选择合适的语言. 我们可以带着下面问题来阅读本文章:1.hadoop都包含什么技术?2.Cloudera公司与hadoop的关系是什么,都有什么产品,产品有什么特性?3.Spark与hadoop的关联是什么?4.Storm与hadoop的关联是什么? h