top K問題研究

問題描述

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

例如,在搜索引擎中,統計搜索最熱門的10個查詢詞;在歌曲庫中統計下載最高的前10首歌等。

解決方法

1. 分治+Trie樹

2.hash+小頂堆(最小堆)

案例

1.有1億個浮點數,如果找出期中最大的10000個?

       1.1、將數據全部排序,然後在排序後的集合中進行查找

        最快的排序算法的時間複雜度一般爲O(nlogn),如快速排序。但是在32位的機器上,每個float類型佔4個字節,1億個浮點數就要佔用400MB的存儲空間,對於一些可用內存小於400M的計算機而言,很顯然是不能一次將全部數據讀入內存進行排序的。其實即使內存能夠滿足要求(我機器內存都是8GB),該方法也並不高效,因爲題目的目的是尋找出最大的10000個數即可,而排序卻是將所有的元素都排序了,做了很多的無用功。

        1.2、局部淘汰法——該方法與排序方法類似,用一個容器保存前10000個數,然後將剩餘的所有數字——與容器內的最小數字相比,如果所有後續的元素都比容器內的10000個數還小,那麼容器內這個10000個數就是最大10000個數。如果某一後續元素比容器內最小數字大,則刪掉容器內最小元素,並將該元素插入容器,遍歷完這1億個數,得到的結果容器中保存的數即爲最終結果了。此時的時間複雜度爲O(n+m^2),其中m爲容器的大小,即10000。

        1.3、分治法——將1億個數據分成100份,每份100萬個數據,找到每份數據中最大的10000個,最後在剩下的100*10000個數據裏面找出最大的10000個。

        如果100萬數據選擇足夠理想,那麼可以過濾掉1億數據裏面99%的數據。100萬個數據裏面查找最大的10000個數據的方法如下:用快速排序的方法,將數據分爲2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大堆個數N小於10000個,就在小的那堆裏面快速排序一次,找第10000-n大的數字;遞歸以上過程,就可以找到第1w大的數。參考上面的找出第1w大數字,就可以類似的方法找到前10000大數字了。此種方法需要每次的內存空間爲10^64=4MB,一共需要101次這樣的比較。

        1.4、Hash法——如果這1億個數裏面有很多重複的數,先通過Hash法,把這1億個數字去重複,這樣如果重複率很高的話,會減少很大的內存用量,從而縮小運算空間,然後通過分治法或最小堆法查找最大的10000個數。

        1.5、最小堆——首先讀入前10000個數來創建大小爲10000的最小堆,建堆的時間複雜度爲O(m)(自下而上的構建,m爲數組的大小即爲10000),然後遍歷後續的數字,並於堆頂(最小)數字進行比較。如果比最小的數小,則繼續讀取後續數字;如果比堆頂數字大,則替換堆頂元素並重新調整堆爲最小堆。整個過程直至1億個數全部遍歷完爲止。然後按照中序遍歷的方式輸出當前堆中的所有10000個數字。該算法的時間複雜度爲O(n^2),空間複雜度是10000(常數)。

實際應用:

(1)單機+單核+足夠大內存

        如果需要查找10億個查詢次(每個佔8B)中出現頻率最高的10個,考慮到每個查詢詞佔8B,則10億個查詢次所需的內存大約是10^9 * 8B=8GB內存。如果有這麼大內存,直接在內存中對查詢次進行排序,順序遍歷找出10個出現頻率最大的即可。這種方法簡單快速,實用。然後,也可以先用HashMap求出每個詞出現的頻率,然後求出頻率最大的10個詞。

(2)單機+多核+足夠大內存

        這時可以直接在內存總使用Hash方法將數據劃分成n個partition,每個partition交給一個線程處理,線程的處理邏輯同(1)類似,最後一個線程將結果歸併。

該方法存在一個瓶頸會明顯影響效率,即數據傾斜。每個線程的處理速度可能不同,快的線程需要等待慢的線程,最終的處理速度取決於慢的線程。而針對此問題,解決的方法是,將數據劃分成c×n個partition(c>1),每個線程處理完當前partition後主動取下一個partition繼續處理,知道所有數據處理完畢,最後由一個線程進行歸併。

(3)單機+單核+受限內存

        將原數據文件切割成一個一個小文件,用hash(x)%M,將原文件中的數據切割成M小文件,如果小文件仍大於內存大小,繼續採用Hash的方法對數據文件進行分割,知道每個小文件小於內存大小,這樣每個文件可放到內存中處理。採用(1)的方法依次處理每個小文件。

(4)多機+受限內存

        這種情況,爲了合理利用多臺機器的資源,可將數據分發到多臺機器上,每臺機器採用(3)中的策略解決本地的數據。可採用hash+socket方法進行數據分發。

問題描述

(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萬個身份證號以及他們對應的數據,身份證號可能重複,找出出現次數最多的身份證號。

重複問題

在海量數據中查找出重複出現的元素或者去除重複出現的元素也是常考的問題。針對此類問題,一般可以通過位圖法實現。例如,已知某個文件內包含一些電話號碼,每個號碼爲8位數字,統計不同號碼的個數。

本題最好的解決方法是通過使用位圖法來實現。8位整數可以表示的最大十進制數值爲99999999。如果每個數字對應於位圖中一個bit位,那麼存儲8位整數大約需要99MB。因爲1B=8bit,所以99Mbit摺合成內存爲99/8=12.375MB的內存,即可以只用12.375MB的內存表示所有的8位數電話號碼的內容。

 

 

 



 



 



 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章