大資料Top K 總結

2021-10-07 06:34:51 字數 4789 閱讀 4271

目錄

top k 問題

1億個數字中找出最大或最小的前100個數字

方法1:全部排序

方法2:區域性淘汰法

插入容器後的操作

區域性淘汰法的去重

方法3:分治法

分治-快排劃分

分治-排序

分治-堆排序

合併結果

方法4:hash法

bitmap

計數排序

方法5:最小堆

實際場景

(1)單機+單核+足夠大記憶體

(2)單機+多核+足夠大記憶體

(3)單機+單核+受限記憶體

(4)多機+受限記憶體

hadoop

最容易想到的方法是將資料全部排序,然後在排序後的集合中進行查詢,最快的排序演算法的時間複雜度一般為o(nlogn),如快速排序。但是在32位的機器上,每個float型別佔4個位元組,1億個浮點數就要占用400mb的儲存空間,對於一些可用記憶體小於400m的計算機而言,很顯然是不能一次將全部資料讀入記憶體進行排序的。其實即使記憶體能夠滿足要求(我機器記憶體都是8gb),該方法也並不高效,因為題目的目的是尋找出最大的10000個數即可,而排序卻是將所有的元素都排序了,做了很多的無用功。

時間o(nlogn),10000億

記憶體o(n),注意:1億大約是100*1024*1024=100mb,1億int或者float為400mb

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

注意:此方法不成熟,沒有說明插入容器後,應該如何操作!

時間o(n*k+m^2),其中m為容器的大小,k為插入容器後的運算元,1億k+10000^2=(k+1)億

記憶體o(m),10000個int,10000大約是10*1024=10kb,1萬int或者float為40kb

操作1:堆,如同方法5

操作2:氣泡排序,新加的數,遍歷陣列,塞入其中,其餘位移動,單次的k為m,這種方法

時間o(n*m+m^2)=10001億,記憶體還是10000個int

如果容器要去重,就建立兩個容器,乙個是hashset,乙個是陣列/堆,插入容器前,先去hashset裡面檢視是否有這個數

第三種方法是分治法,將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大數字了。(根據第10000大的數字,進行快排劃分)

此種方法一共需要101次這樣的比較,記憶體要存100萬個int,4mb

對100萬個數進行快速排序

時間,單次要o(n * log n),100萬*1000=10億,總共1010億

使用10000的堆,進行排序

時間,單次要o(n * log m),100萬*100=1億,總共101億,可以看到資料量大時,此方法更好

可以使用多路歸併演算法,1路與1路進行合併,然後再合併一路。。。

也可以把結果混在一起,再次使用上面的快排劃分/排序/堆排序

第四種方法是hash法。如果這1億個書裡面有很多重複的數,先通過hash法,把這1億個數字去重複,這樣如果重複率很高的話,會減少很大的記憶體用量,從而縮小運算空間,然後通過分治法或最小堆法查詢最大的10000個數。

可以使用採用bitmap來進行去重。

乙個char型別的資料為乙個位元組也就是8個字元,而每個字元都是用0\1標識,我們初始化所有字元為0。

我們申請n/8+1容量的char陣列,總共有n+8個字元。

對資料進行遍歷,對每個元素s進行s/8操作獲得char陣列中的下標位置,s%8操作獲得該char的第幾個字元置1。

在遍歷過程中,如果發現對應的字元位置上已經為1,則代表該值為重複值,可以去除。

如果要得到該數的數目,使用計數排序,原來乙個數字代表乙個字元0/1現在1個數字代表乙個int,32位,記憶體擴大32倍。

兩種方法去重,所需時間為o(n),1億

記憶體,32位的int,總共40億左右,計數排序為40億int,16gb,bitmap為40億bit,400mb

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

該演算法的時間複雜度為o(n*logm),100億

記憶體,o(m) , 10000個int,40kb

實際上,最優的解決方案應該是最符合實際設計需求的方案,在時間應用中,可能有足夠大的記憶體,那麼直接將資料扔到記憶體中一次性處理即可,也可能機器有多個核,這樣可以採用多執行緒處理整個資料集。

下面針對不容的應用場景,分析了適合相應應用場景的解決方案。

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

這時可以直接在記憶體總使用hash方法將資料劃分成n個partition,每個partition交給乙個執行緒處理,執行緒的處理邏輯同(1)類似,最後乙個執行緒將結果歸併。

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

這種情況下,需要將原資料檔案切割成乙個乙個小檔案,如次啊用hash(x)%m,將原檔案中的資料切割成m小檔案,如果小檔案仍大於記憶體大小,繼續採用hash的方法對資料檔案進行分割,直到每個小檔案小於記憶體大小,這樣每個檔案可放到記憶體中處理。採用(1)的方法依次處理每個小檔案。或者使用堆排序或者分治法處理。(當記憶體較小時)

這種情況,為了合理利用多台機器的資源,可將資料分發到多台機器上,每台機器採用(3)中的策略(就是分治+堆排序/快排)解決本地的資料。可採用hash+socket方法進行資料分發。

從實際應用的角度考慮,(1)(2)(3)(4)方案並不可行,因為在大規模資料處理環境下,作業效率並不是首要考慮的問題,演算法的擴充套件性和容錯性才是首要考慮的。

演算法應該具有良好的擴充套件性,以便資料量進一步加大(隨著業務的發展,資料量加大是必然的)時,在不修改演算法框架的前提下,可達到近似的線性比;演算法應該具有容錯性,即當前某個檔案處理失敗後,能自動將其交給另外乙個執行緒繼續處理,而不是從頭開始處理。

top k問題很適合採用mapreduce框架解決,使用者只需編寫乙個map函式和兩個reduce 函式,然後提交到hadoop(採用mapchain和reducechain)上即可解決該問題。具體而言,就是首先根據資料值或者把資料hash(md5)後的值按照範圍劃分到不同的機器上,最好可以讓資料劃分後一次讀入記憶體,這樣不同的機器負責處理不同的數值範圍,實際上就是map。得到結果後,各個機器只需拿出各自出現次數最多的前n個資料,然後彙總,選出所有的資料**現次數最多的前n個資料,這實際上就是reduce過程。對於map函式,採用hash演算法,將hash值相同的資料交給同乙個reduce task;對於第乙個reduce函式,採用hashmap統計出每個詞出現的頻率,對於第二個reduce 函式,統計所有reduce task,輸出資料中的top k即可。

直接將資料均分到不同的機器上進行處理是無法得到正確的結果的。因為乙個資料可能被均分到不同的機器上,而另乙個則可能完全聚集到乙個機器上,同時還可能存在具有相同數目的資料。

大資料處理之(top k)

top k 簡介 在大量資料中找出重複次數最多的前k個。問題分析 聽起來這個問題十分簡單,只需對這些資料進行一次排序即可得到前k個。如果這樣的話,首先得定義乙個資料結構來儲存這些資料,大量的資料會消耗過大的程序資源,甚至 耗盡 程序的資源。還有乙個問題是排序的時間複雜度是非常高的,一般來說,較快的排...

大資料下的多維TopK演算法

在數週前所發表的博文 大資料下的topk演算法 中介紹了求解大資料時代中幾乎是最為經典的topk的過程。雖然大資料技術使得大規模資料下的topk問題得到了有效的解決,但是對於一些該問題的拓展,單單靠大資料技術是無法獲得令人滿意的解決方案。本文所述的多維資料下的topk問題就是這一類問題。如果該演算法...

top K 演算法總結

問題描述 有 n n 1000000 個數,求出其中的前k個最小的數 又被稱作topk問題 將n個數進行完全排序,從中選出排在前k的元素即為所求。有了這個思路,我們可以選擇相應的排序演算法進行處理,目前來看快速排序,堆排序和歸併排序都能達到 o nlogn 的時間複雜度。可以採用資料池的思想,選擇其...