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

在工作面试中,经常会出现考察海量数据处理的问题:给你若干个数,从其中找到出现次数最多的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过程模拟_第1张图片


此处主要讨论TopK算法: 
主要思想,首先读取K个元素建立最小堆,再依次读取文件内容,若读出的元素val大于堆顶,则将堆顶元素替换掉并进行堆调整。最后剩下的一定是最大的K个元素。 
输入:统计结果文件,格式 “key val”
输出:前TopK个键值对  
TopK
int* 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  195
513  196
653  198
608  196
603  198
63   199
575  199
123  204
1005 203
23   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

你可能感兴趣的:(从海量数据中提取TopK过程模拟)