互联网_大数据
大数据的题目其实很简单,一般就是几种思路:
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;
#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); } }
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过滤器)转换为一个查找操作?<具体实现?>