从海量数据中提取TopK过程模拟

来源:互联网 发布:快手软件大全 编辑:程序博客网 时间:2024/05/17 08:22
在工作面试中,经常会出现考察海量数据处理的问题:给你若干个数,从其中找到出现次数最多的K个数据。 
(百度面试题中,有N多IP地址,让你找访问量TopK的IP) 

解决方法


1. 有大量重复数据 
如果有大量重复数据,可以利用map,遍历文件,一次性将所有不同数据载入内存,对于重复数据,出现次数++。读取完毕后,利用堆排序,提取出topK即可。 

2. 重复数据很少 
文件很大,无法一次读入内存,方法是将文件先进行hash切割,切割后对每个小文件进行TopK统计,最后进行全局topK统计。 


实例验证


此处自己做了一个完整的程序,简单的理解了下对这种问题的处理方式 

1. 数据生成

利用随机数,生成1000*1000*100个整数的数据文件(数据量可以更多)

//生成数据文件void generateTestData(int len){FILE*  file = fopen(inputFileName, "w");srand((unsigned int)time(0));for (int i = 0; i < len; i++){int val = rand();fprintf(file, "%d ", val);}}

 

2. 文件进行切割

根据HASH函数,将数据均匀分布到不同的文件中(相同数据在同一个文件中) 
HASH函数:

//hash函数unsigned int hashFunction(unsigned int key){key += ~(key << 15);key ^=  (key >> 10);key +=  (key << 3);key ^=  (key >> 6);key += ~(key << 11);key ^=  (key >> 16);return key;}

 文件分割函数(该函数效率很低!)
说明:根据数值内容生成100以内的HASH值,以HASH值为名创建文件。
若 hashFunction(key) = hashval,那么key就放在该HASH文件中。

void seperate_file(){FILE* inputFile = fopen(inputFileName.c_str(), "r");char seperatedName[MAX_FILE_LEN];memset(seperatedName, 0, MAX_FILE_LEN);while (!feof(inputFile)){int val;fscanf_s(inputFile, "%d", &val);unsigned int hashKey = hashFunction(val);hashKey %= 100;memset(seperatedName, 0, MAX_FILE_LEN);_itoa(hashKey, seperatedName, 10);filenames.insert(seperatedName);FILE* splitFile = fopen(seperatedName, "a+");fprintf(splitFile, "%d ", val);fclose(splitFile);}}

 

3. 单个文件TopK统计

统计逻辑:



此处主要讨论TopK算法: 
主要思想,首先读取K个元素建立最小堆,再依次读取文件内容,若读出的元素val大于堆顶,则将堆顶元素替换掉并进行堆调整。最后剩下的一定是最大的K个元素。 
输入:统计结果文件,格式 “key val”
输出:前TopK个键值对  
TopKint* keys = new int[K];int* vals = new int[K];count = 0;while (!eof())    read key, val    if count < k then         keys[count] = key         vals[count] = val                  if count = K - 1 then            buildHeap(keys, vals, 0, count)    //根据val建最小堆,keys随vals调整         end if    else         if val > vals[0] then                //替换堆顶,并调整堆            vals[0] = val            keys[0] = key            adjustHeap(keys,vals,0,K-1)         end if       end if    count++end while

 

4. 对汇总文件进行TopK统计

汇总文件中汇集每个小文件中的TopK数据,对其再来一次TopK统计,就可以得到整体的TopK数据。

数值  出现次数452  195513  196653  198608  196603  19863   199575  199123  2041005 20323   198

 注:文件切割的功能太简单,也太低效~ 需要更好的办法

 

附:建堆以及调整堆代码

/*data[p+1......r]都符合小顶堆,只有data[p]不符合,进行调整*/void adjustHeap(int* keys, int* vals, int p, int r){if (p == r) return;int val = vals[p];int key = keys[p];int curIndex = p;int childIndex = 2*p;for ( ; childIndex <=r; childIndex = childIndex*2){int leftChildVal = vals[childIndex];if (childIndex+1 <= r){int rightChildVal = vals[childIndex + 1];if (rightChildVal < leftChildVal)childIndex = childIndex + 1;}if (val <= vals[childIndex])break;vals[childIndex/2] = vals[childIndex];keys[childIndex/2] = keys[childIndex];}vals[childIndex/2] = val;keys[childIndex/2] = key;}//建小顶堆void buildHeap(int* keys, int* vals,  int heapSize){for (int i = heapSize/2; i >=0; i--)   adjustHeap(keys, vals, i, heapSize-1);}
http://m.oschina.net/blog/74147
原创粉丝点击