海量数据处理

来源:互联网 发布:网络驱动没了怎么办 编辑:程序博客网 时间:2024/06/08 08:52


海量数据处理:
(一)单位换算:

1k=2^10=10^3
1M=2^20=10^6
1G=2^30=10^9

(二)海量数据的处理问题:

(1)数据量大导致时间长:
针对时间,可以使用Hash、bit-map,堆,快速排序,归并排序,trie树
(2)数据量大导致内存不够:
针对空间,只有分而治之/hash映射(大而化小)

(3)常见的海量问题:
1.海量数据中TopK问题;
2.海量数据中求取出现次数最多的1个/N个:
3.海量数据中不重复的元素个数;
4.海量数据中取出重复的元素:
5.海量数据排序;
6.海量数据按照出现个数排序;


(三)处理海量数据的方法总结:

(1)分而治之/hash映射 + hash统计 + 堆/快速/归并排序;
(求取TopN,使用堆)
(2)分而治之/hash映射+bit-map方法:

(是否存在,两个文件的公共元素,排除重复,统计不重复个数,无重复数据排序)
(3)trie树/hash:(统计个数,排除重复)


(4)双层桶划分:(按大小划分为多个区域)

(四)分而治之/hash映射 + hash统计 + 堆/快速/归并排序的使用;

(1)问题一:

1)问题:
海量日志数据,提取出某日访问百度次数最多的那个IP。

2)解析:

1.hash映射/分而治之:
将大文件分解为1000个小文件,扫描所有的ip,然后将hash值为0的分到一个文件中,记为文件0,hash值为1的记录到文件1中。。。。。。(因为存在重复的ip,相同的ip必然会在同一个文件中,所以比较容易计算ip的个数)
2.hash计数:
对于每一个小文件,可以构建一个IP为key,出现次数为value的Hash_map。
3.堆排序/快速排序:
此中只需要扫描每个小文件,即可就可以得到每个小文件的最大值。
4.然后1000个小文件的最大值合并,再去最大值即可。

3)方法三:计数法

1.思想:
假设一天之内某个IP访问百度的次数不超过40亿次,则访问次数可以用unsigned表示.用数组统计出每个IP地址出现的次数, 即可得到访问次数最大的IP地址。
(2^32=2^2*2^30=4G=40亿)

IP地址是32位的二进制数,共有N=2^32=4G个不同的IP地址, 创建一个unsigned count[N]的数组;而sizeof(count) == 4G*4=16G>4G(计算机内存),不可以直接创建这个数组。

假设允许使用的内存是512M, 512M/4=128M (每个ip32位,4B),即512M内存可以统计128M个不同的IP地址的访问次数,N/128M =4G/128M = 32 .

所以:只要把IP地址划分成32个不同的区间,分别统计出每个区间中访问次数最大的IP, 然后就可以计算出所有IP地址中访问次数最大的IP了.

因为2^5=32, 所以可以把IP地址的最高5位作为区间编号,把相同区间的IP地址保存到同一的临时文件中.

2.举例:
如: ip1=0x1f4e2342,ip1的高5位是id1 = ip1 >>27 = 0x11 = 3, ip1的其余27位是value1 = ip1 &0x07ffffff = 0x074e2342,所以把 value1 保存在tmp3文件中。由id1和value1可以还原成ip1, 即 ip1 =(id1<<27)|value1。

按照上面的方法可以得到32个临时文件,每个临时文件中的IP地址的取值范围属于[0-128M)
128M=2^7* 2^20=2^27(即IP的低27位),因此可以统计出每个IP地址的访问次数.从而找到访问次数最大的IP地址。

3.代码实现如下:

http://blog.csdn.net/legend050709/article/details/38982997

(2)问题二:

1)问题:有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

2)分析:
1.分而治之/hash映射:
顺序读文件中,对于每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(记为x0,x1,...x4999)中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。

2.hash统计:
对每个小文件,采用trie树/hash_map等统计每个文件中出现的词以及相应的频率。

3.堆/归并排序:
取出出现频率最大的100个词(可以用含100个结点的最小堆),并把100个词及相应的频率存入文件,这样又得到了5000个文件。最后就是把这5000个文件进行建立最小堆的过程。


(3)问题三:
1)问题:
有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。

2)解析:
1.hash映射:
顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。

2.hash统计:
找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。注:hash_map(query,query_count)是用来统计每个query的出现次数,不是存储他们的值,出现一次,则count+1。

3.堆/快速/归并排序:
利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。对这10个文件进行归并排序(内排序与外排序相结合)。


(4)问题四:
1)问题:
给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

2)解析:
1.分而治之/hash映射:
遍历文件a,对每个url求取,然后根据所取得的值将url分别存储到1000个小文件(记为)中。这样每个小文件的大约为300M。

2.遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为)。这样处理后,所有可能相同的url都在对应的小文件()中,不对应的小文件不可能有相同的url。

3.只要求出1000对小文件中相同的url即可。
4.每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

-------
(五)分而治之+bit-map方法:(适合判断是否重复,以及是否存在)
(1)问题一:
1)问题:

2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

2)解析:
整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了。

3)代码实现:

#include<stdio.h>
#include<memory.h>
//用char数组存储2-Bitmap。
//2-bit,00表示没有出现,01表示出现一次,10表示出现多次,不用考虑大小端内存的问题
unsigned char flags[1000]; //数组大小自定义

unsigned get_val(int idx)
{
    int i = idx/4;
    int j = idx%4;
    unsigned ret = (flags[i]&(0x3<<(2*j)))>>(2*j);
    return ret;
}

unsigned set_val(int idx, unsigned int val)
{
    int i = idx/4;
    int j = idx%4;
    unsigned tmp = (flags[i]&~((0x3<<(2*j))&0xff)) | (((val%4)<<(2*j))&0xff);
    flags[i] = tmp;
    return 0;
}
unsigned add_one(int idx)
{
    if (get_val(idx)>=2) {
        return 1;
    }
    else  {
        set_val(idx, get_val(idx)+1);
        return 0;
    }
}

//只测试非负数的情况;
//假如考虑负数的话,需增加一个2-Bitmap数组.
int a[]={1, 3, 5, 7, 9, 1, 3, 5, 7, 1, 3, 5,1, 3, 1,10,2,4,6,8,0};

int main()
{
    int i;
    memset(flags, 0, sizeof(flags));
   
    printf("原数组为:");
    for(i=0;i < sizeof(a)/sizeof(int); ++i)  {
        printf("%d  ", a[i]);
        add_one(a[i]);
    }
    printf("\r\n");

    printf("只出现过一次的数:");
    for(i=0;i < 100; ++i)  {
        if(get_val(i) == 1)
            printf("%d  ", i);
    }
    printf("\r\n");

    return 0;
}

(2)问题二:
1)问题:
在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。
2)解析:
方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
 方案2:也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。

 (3)问题三
 1)问题:
 给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?

 2)分析:
   第一反应时快速排序+二分查找。以下是其它更好的方法:  

      方案1:frome oo,用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。

(4)问题四:
1)问题:给40亿个unsigned int的整数,如何判断这40亿个数中哪些数重复?
2)解析:
申请512M的内存空间,利用1-bit-map,初始化时每位均为0,第一次读取某个数据,则相应的位为0,然后0变为1;第二次在读取该数据,则相应为已经为1,所以可以判断重复。
-------

(六)双层桶划分:(按照大小划分为多个区域)

(1)概念:
本质上还是分而治之的思想,重在“分”的技巧上!

(2)基本原理:
因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。可以通过多次缩小,双层只是一个例子。

(3)举例一:

1)问题:
5亿个int找它们的中位数。

2)解析:
1.int为32位,表示范围为-2^31~2^31-1,将int划分为2^16个区域,每个区域存储是数据范围都是2^32/(2^16)=2^16,所以0号区域存放的是-2^32~-2^32+2^16这个范围的数据,同理,1号区域存放的数据的范围也是2^16,。。。一直到2^16区域。
2.扫描5亿个数据,将相应的数据放到相应的区域内。
3.然后统计每个区域的个数。
4.计算出中位数落到了区域i,然后在对区域i进行划分,扫描。
5.直至找到中位数。

(4)举例二:

1)问题:
2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
2)解析:
见上面。


------
(七)找到两个海量文件中的公共元素:

(1)问题:
在本地磁盘里面有file1和file2两个文件,每一个文件包含500万条随机整数(可以重复),最大不超过2147483648也就是一个int表示范围。要求写程序将两个文件中都含有的整数输出到一个新文件中。
(2)代码实现:
http://blog.csdn.net/legend050709/article/details/38982365

0 0
原创粉丝点击