互联网_大数据

大数据的题目其实很简单,一般就是几种思路:

1,分治——Hash映射

2,位图bitmap

3,Bloom Filter

4,倒排索引

 

1,分治——hash映射

常用的hash函数

所有hansh函数的基本特性都是,如果两个输出的散列值是不同的,那么其对应的输入也一定是不同的。

常用的hash算法包括:MD4,MD5(二者都是固定的128位),SHA-1;<之前的印象只不过是hash用于加密罢了>;之前学过简单的hash算法还有:直接定址法,数字分析法,除留余数发,折叠法,平方取中法等等。

hash冲突的解决方法

线性探测法、二次探测法、伪随机探测法、再散列法、链地址法、建立一个公共溢出区等方法

分治法——hash在大数据处理中的运用

思路:在进行大文件处理的时候,若文件过大,无法一次性读入内存,可以考虑采用hash映射的方法将文件中的元素映射到小文件中(保证同样的元素映射到同一个文件中),然后依次处理各个小文件,最后合并处理结果

题目一:给定a、b两个文件,各存放了50亿个url,每一个url占用64个字节,内存限制是4G,请找出两个文件中共通的url

题解:

分析:每个文件的大小是50亿*64=320G,远远大于内存限制的4G,因此需要将其拆分到小文件中进行处理。

1)对文件a,对每个url采取Hash(url)%1024的方法,然后根据所取得的值将url分别存储到1024个小文件中,这样每一个小文件大概是300M

2)对于文件b,采取和a同样的方式将url分别存储到1024个小文件中,这样处理之后,所有可能的url都在对应的小文件(a0 vs b0,a1 vs b1 等等),不对应的小文件不可能有相同的url(不需要对a中的数据去比对b中的所有小文件),然后我们只需要对对应的小文件进行处理即可

3)对于每对小文件相同的url,可以把其中一个小文件的url存储到hashSet中,然后遍历另一个小文件的url,看其是否在刚才构建的hashset中,如果是,那么就是共同的url

 

题目二:有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query可能重复,请你按照query的频度进行排序。

方法一:

1)利用hash(key)%10的方法将query分拆到10个其他的文件中a0,a1,...a9,这样生成的文件每个大概1G(数据倾斜的问题这里不考虑)

2)利用2G内存左右的机器,利用hashmap统计每个query出现的次数,再对每个文件a0...a9内部的数据采用快排/堆/归并等方法进行排序,将排好序的结果输出到对应的文件中,从而得到10个排好序的文件b0,b1...b9

3)对于排好序的这些文件进行归并排序(也可以利用败者树进行多路归并)比如第一次10个文件中各读出一条记录,进行排序,之后独处一个插入一个么?

方法二:

一般query的总量总是有限的,只是重复的次数比较多。如果所有的query都可以一次性加载到内存,那么可以采用trie树/hashMap等来直接统计每个query出现的次数,然后按照次数进行快速排序

 

TOPK问题

堆排序,时间复杂度是O(nlogk),空间复杂度是O(1)

不过记得如果需要统计频次,很可能存在不可能一次性加载到内存的情况,就需要首先利用Hash(x)%n的方法将其分拆到小文件中,然后每一个小文件利用hashmap统计次数

例如:海量日志数据,提取出某日访问百度次数最多的那个ip,假设当前机器的可用内存较小。

 

2,位图——bitmap

位图的实现

c++版本,利用除法判断应当存储在具体哪一个int数字,利用%判断存储在哪一位 

#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<stdint.h>  
#define BYTE_SIZE 8  
#define BIT_MAP_SIZE 12500000  
#define INPUT_DATA_SIZE 10  
  
void add_data(uint8_t *bit_map, uint32_t data)  
{  
    bit_map += (data/BYTE_SIZE);  
    *bit_map = *bit_map|(0x01<<(data%BYTE_SIZE));  
}  
  
  
void find_data(uint8_t *bit_map, uint32_t data)  
{  
    bit_map += (data/BYTE_SIZE);  
    if((*bit_map)&(0x01<<(data%BYTE_SIZE)))  
    {  
        printf("THE DATA %ld IS IN DATABASE\n", data);  
    }  
    else  
    {  
        printf("THE DATA %ld IS NOT IN DATABASE\n", data);  
    }  
}  
  
void delete_data(uint8_t *bit_map, uint32_t data)  
{  
    bit_map += (data/BYTE_SIZE);  
    if((*bit_map)&(0x01<<(data%BYTE_SIZE)))  
    {  
        *bit_map = (*bit_map)&(~(0x01<<(data%BYTE_SIZE)));  
        printf("THE DATA %ld IS DELETED FROM DATABASE\n", data);  
    }  
    else  
    {  
        printf("THE DATA %ld IS NOT IN DATABASE\n", data);  
    }  
}  
  
int main()  
{  
    uint8_t bit_map[BIT_MAP_SIZE];  
    uint32_t datap[INPUT_DATA_SIZE] = {88081234, 88081235, 88082234, 88082235, 88080790, 88080750, 88080779, 88088321, 88088339, 88088349};  
    int i = 0;  
    uint32_t data;  
  
  
    memset(bit_map, 0, BIT_MAP_SIZE);  
      
    for(i = 0; i < INPUT_DATA_SIZE; i++)  
    {  
        add_data(bit_map, datap[i]);  
    }  
  
  
    printf("INPUT FIND DATA:\n");  
    scanf("%ld", &data);  
    find_data(bit_map, data);  
        printf("INPUT DELETE DATA:\n");  
        scanf("%ld", &data);  
        delete_data(bit_map, data);  
        printf("INPUT FIND DATA:\n");  
        scanf("%ld", &data);  
        find_data(bit_map, data);  
      
  
    return 0;  
bitmap的c++实现

  

#define BITWORD 32
#define SHIFT 5
#define MASK 0x1F

//一点技巧:乘以2的n次方可亿右移;除以2的n次方可以左移;对2的n次方取余可以喝0x0.。01.。。1进行相与得到
class Bitset{
        int * bitset;
public:
        void Bitset(int size){
                bitset=new int[size/BITWORD+1];
                memset(bitset,0,sizeof(int)*(size/BITWORD+1));
        }

        void set(int i){
                bitset[i>>SHIFT]&=(1<<(i&MASK));
        }

        void clr(int i){
                bitset[i>>SHIFT]&=~(1<<(i&MASK));
        }

        int get(int i){
                return bitset[i>>SHIFT]&(1<<(i&(MASK)));
        }
        ~Bitset(){
                delete[] bitset;
        }
}

//检验是否存在数据的重复
void checkDepulicate(int* array,int length){
        int i,j;
        Bitset bitset(length);

        for(i=0;i<array,i++){
                int num=array[i];
                if(bitset.get(num)!=0){
                cout<<num<<endl;
        }else{
                bitset.set(i);
        }

}
bitset的c++实现(注意对2的n次方乘法,除法和取模的技巧)

 

   java中实现位图很简单,已经封装好了一个BitSet的类

  应用一:用于去重

题目一:已知某个文件内包含一些电话号码,每个号码是8位数字,统计不同号码的个数

分析:8位数字有0-99999999.一共1亿个,需要内存大概是12M。依次读入每个电话号码,然后将bitmap的响应位置置1,最后统计1的个数即可。

题目二:给40亿个互相不重复的unsigned int的整数,没排过序,然后再给一个数,如何快速判断这个数是否在40亿个数当中?

1)unsigned int最多有2的32次方个数,申请512M内存(512*2的10次方*8=2的32次方),也就是一位标志一个unsigh int数的话,可以标志所有的unsigned int类型的数

2)依次读入这40亿个数,置对应位为1,然后读入需要查询的数字,看对应位置是否为1.

题目三:在2.5亿个整数中查找只出现一次的整数,内存有限。

分析:这里不是有或者无,重复或者不重复两种状态,而是有出现0次,出现1次,出现多次等多种状态,因此采用两位进行记录。

方案:采用2-bitmap(每个数分配2bit,00代表不存在,01代表一次,11代表多次),需要内存2的32次方*2bit=1G。然后依次扫描这2.5亿的整数,将对应位置是01的输出即可。

 

3,bloom filter

 

 

 

 

 

 

 

 

 

1,给定两个整数集合A和B,每个集合都包含20亿个不同整数,请给出快速计算A∩B的算法,算法可使用外存,但是要求占用内存不能超过4GB。

答:

基本思路:利用bitmap以及位运算来实现。

思考过程:整数最大为2的32次方-1;如果每位依次记录一个数,那么需要int的个数是(2的32次方-1)/32=1亿个。占用的内存大小为4byte*1亿=0.4G。不超过题目要求的4G.

因此,解决思路是:

1)申请两个[2的32次方-1]/32个int型的整数数组

2)依次扫描两个集合A和B,如果集合包含某一个整数,就将对应位置1

3)之后将两个用作标志位的两个整形数组做交运算

思考,如果是两个集合中都包含20亿个url呢,如何求出二者的交集(利用bloom过滤器)转换为一个查找操作?<具体实现?>

 

 

posted @ 2014-08-22 12:04  bobo的学习笔记  阅读(382)  评论(0编辑  收藏  举报