從海量數據中提取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算法:
主要思想,首先讀取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);
}
相關文章
相關標籤/搜索