您當前的位置:首頁 > 歷史

海量資料中找出前k大數(topk問題)

作者:由 dilligencer 發表于 歷史時間:2020-03-28

在海量資料中找出出現頻率最好的前k個數,或者從海量資料中找出最大的前k個數,這類問題通常被稱為top K問題。

針對top K類問題,通常比較好的方案是分治+Trie樹/hash+小頂堆(就是上面提到的最小堆),即先將資料集按照Hash方法分解成多個小資料集,然後使用Trie樹或者Hash統計每個小資料集中的query詞頻,之後用小頂堆求出每個資料集中出現頻率最高的前K個數,最後在所有top K中求出最終的top K。

方法進階:

1、最簡單的方法就是快排,取topk

2、區域性淘汰法。用一個容器儲存前k個數,然後將剩餘的所有數字——與容器內的最小數字相比,如果所有後續的元素都比容器內的k個數還小,那麼容器內這k個數就是最大k個數。如果某一後續元素比容器內最小數字大,則刪掉容器內最小元素,並將該元素插入容器,最後遍歷完所有的數,得到的結果容器中儲存的數即為最終結果了

3、分治法。將1億個資料分成100份,每份100萬個資料,找到每份資料中最大的10000個,最後在剩下的100*10000個數據裡面找出最大的10000個。100萬個資料裡面查詢最大的10000個數據的方法如下:用快速排序的方法,將資料分為2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大堆個數N小於10000個,就在小的那堆裡面快速排序一次,找第10000-n大的數字;遞迴以上過程,就可以找到第1w大的數。參考上面的找出第1w大數字,就可以類似的方法找到前10000大數字了。此種方法需要每次的記憶體空間為10^6*4=4MB,一共需要101次這樣的比較。

4、採用最小堆。首先讀入前10000個數來建立大小為10000的最小堆,建堆的時間複雜度為O(mlogm)(m為陣列的大小即為10000),然後遍歷後續的數字,並於堆頂(最小)數字進行比較。如果比最小的數小,則繼續讀取後續數字;如果比堆頂數字大,則替換堆頂元素並重新調整堆為最小堆。整個過程直至1億個數全部遍歷完為止。然後按照中序遍歷的方式輸出當前堆中的所有10000個數字。該演算法的時間複雜度為O(nmlogm),空間複雜度是10000(常數)。

以下是一些經常被提及的該類問題。

(1)有10000000個記錄,這些查詢串的重複度比較高,如果除去重複後,不超過3000000個。一個查詢串的重複度越高,說明查詢它的使用者越多,也就是越熱門。請統計最熱門的10個查詢串,要求使用的記憶體不能超過1GB。

(2)有10個檔案,每個檔案1GB,每個檔案的每一行存放的都是使用者的query,每個檔案的query都可能重複。按照query的頻度排序。

(3)有一個1GB大小的檔案,裡面的每一行是一個詞,詞的大小不超過16個位元組,記憶體限制大小是1MB。返回頻數最高的100個詞。

(4)提取某日訪問網站次數最多的那個IP。

(5)10億個整數找出重複次數最多的100個整數。

(6)搜尋的輸入資訊是一個字串,統計300萬條輸入資訊中最熱門的前10條,每次輸入的一個字串為不超過255B,記憶體使用只有1GB。

(7)有1000萬個身份證號以及他們對應的資料,身份證號可能重複,找出出現次數最多的身份證號。

最小堆

對於每個非葉子節點的數值,一定

不大於

孩子節點的數值。這樣可用含有K個節點的最小堆來儲存K個目前的最大值(當然

根節點

是其中的最小數值)。每次有資料輸入的時候可以先與根節點比較。若不大於根節點,則捨棄;否則用新數值替換根節點數值。並進行最小堆的調整。

海量資料中找出前k大數(topk問題)

Python 小頂堆:

class solution:

def topk(self, inputs, k):

import heapq

if inputs == None or len(inputs) < k or len(inputs) <= 0 or k <= 0:# 注意極限條件的確定

return []

output = []

for number in inputs:

if len(output) < k:

output。append(number)

else:

output = heapq。nlargest(k, output)

print(output)

if number >= output[-1]:

output[-1] = number

else:

continue

return output

標簽: 10000  output  個數  最小  100