1. 程式人生 > >海量資料中找出前k大數(topk問題)

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

前兩天面試3面學長問我的這個問題(想說TEG的3個面試學長都是好和藹,希望能完成最後一面,各方面原因造成我無比想去鵝場的心已經按捺不住了),這個問題還是建立最小堆比較好一些。

先拿10000個數建堆,然後一次新增剩餘元素,如果大於堆頂的數(10000中最小的),將這個數替換堆頂,並調整結構使之仍然是一個最小堆,這樣,遍歷完後,堆中的10000個數就是所需的最大的10000個。建堆時間複雜度是O(mlogm),演算法的時間複雜度為O(nmlogm)(n為10億,m為10000)。

優化的方法:可以把所有10億個資料分組存放,比如分別放在1000個檔案中。這樣處理就可以分別在每個檔案的10^6個數據中找出最大的10000個數,合併到一起在再找出最終的結果。

以上就是面試時簡單提到的內容,下面整理一下這方面的問題:

在大規模資料處理中,經常會遇到的一類問題:在海量資料中找出出現頻率最好的前k個數,或者從海量資料中找出最大的前k個數,這類問題通常被稱為top K問題。例如,在搜尋引擎中,統計搜尋最熱門的10個查詢詞;在歌曲庫中統計下載最高的前10首歌等。 針對top K類問題,通常比較好的方案是分治+Trie樹/hash+小頂堆(就是上面提到的最小堆),即先將資料集按照Hash方法分解成多個小資料集,然後使用Trie樹或者Hash統計每個小資料集中的query詞頻,之後用小頂堆求出每個資料集中出現頻率最高的前K個數,最後在所有top K中求出最終的top K

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

全部排序

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

區域性淘汰法

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

是分治法

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

Hash法。

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

最小堆法

首先讀入前10000個數來建立大小為10000的最小堆,建堆的時間複雜度O(mlogm)(m為陣列的大小即為10000),然後遍歷後續的數字,並於堆頂(最小)數字進行比較。如果比最小的數小,則繼續讀取後續數字;如果比堆頂數字大,則替換堆頂元素並重新調整堆為最小堆。整個過程直至1億個數全部遍歷完為止。然後按照中序遍歷的方式輸出當前堆中的所有10000個數字。該演算法的時間複雜度為O(nmlogm)空間複雜度是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)(2)(3)(4)方案並不可行,因為在大規模資料處理環境下,作業效率並不是首要考慮的問題,演算法的擴充套件性和容錯性才是首要考慮的。演算法應該具有良好的擴充套件性,以便資料量進一步加大(隨著業務的發展,資料量加大是必然的)時,在不修改演算法框架的前提下,可達到近似的線性比;演算法應該具有容錯性,即當前某個檔案處理失敗後,能自動將其交給另外一個執行緒繼續處理,而不是從頭開始處理。

最優方案(使用hadoop)

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即可。

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

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

(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大數topk問題

前兩天面試3面學長問我的這個問題(想說TEG的3個面試學長都是好和藹,希望能完成最後一面,各方面原因造成我無比想去鵝場的心已經按捺不住了),這個問題還是建立最小堆比較好一些。 先拿10000個數建堆,然後一次新增剩餘元素,如果大於堆頂的數(10000中最小

海量資料k大數

前兩天面試3面學長問我的這個問題(想說TEG的3個面試學長都是好和藹,希望能完成最後一面,各方面原因造成我無比想去鵝場的心已經按捺不住了),這個問題還是建立最小堆比較好一些。         先拿10000個數建堆,然後一次新增剩餘元素,如果大於堆頂的數(10000中最小的

海量日誌資料__怎麼在海量資料重複次數最多的一個

 問題一: 怎麼在海量資料中找出重複次數最多的一個 演算法思想: 方案1:先做hash,然後求模對映為小檔案,求出每個小檔案中重複次數最多的一個,並記錄重複次數。         然後找出上一步求出的資料中重複次數最多的一個就是所求(如下)。 問題二:    

在一個字串陣列重複的字串C#

方法一:(hashtable)推薦使用 private bool checkImportDuplicate(string[] str ) { bool flag = false; Hashtable hash = new Hashtabl

TOP-K排序演算法,從海量不重複資料最大/小的K個數

如題,TOP-K排序的主要功能是找出一堆不重複資料中的最小或最大的幾個數,此處我們介紹這種型別題目的某種解法: 最大最小堆,最大堆結構裡面的每一個數不都是小於root的值麼?和我們要解決的問題很像。由此,我們可以構造一個堆,並且用它來儲存我們需要找的那幾個數。有這麼一個動態

如何從大量資料異常值

前言 機器學習中資料預處理階段,首先要考慮的就是將資料集中的異常值找出來,然後再做額外處理。當然,異常值的處理並不存在什麼銀彈,只能具體情況具體分析再根據效果選擇處理方法。 直方圖 看看資料集直方圖也許能看出點端倪,比如下面這個圖,下方的是原始資料集,上面的是對應直方圖,可以看到大多數都分佈在11000

bfprt演算法----陣列最小的k個數Java

 無序陣列中最小的k個數 對於一個無序陣列,陣列中元素為互不相同的整數,請返回其中最小的k個數。 給定一個整數陣列A及它的大小n,同時給定k,請返回其中最小的k個數。 測試樣例: [1,2,4,3],4,2 返回:[1,2] (1)利用堆,時間複雜度O(Nlog

java 實現從無序陣列 k大的數, 無序陣列充許有重複元素

要求找出第幾名的元素是什麼(找出B[i]的值)? 找出第k名的元素的值。          先從A中隨機一個下標index1, 然後進行一趟快速排序等到新陣列A1,排完了就知道index1對應的元素在A1中的新下標index2. 如果k等於index2,則A1[index2]就是要找的值。 如果 k小於in

從倆個有序陣列K小的數。要求時間複雜度O(logmin(m,n))

思路 該題目要求時間複雜度為O(log(min{m,n})) 所以不能直接遍歷任意一個數組這樣時間複雜度就不符合了。也不能對任意一陣列進行二分查詢,因為要求是倆個數組元素合併後的第K小的數,所以直接遍歷用二分遍歷任意一個數組也是行不通的。 故我們可以以

從十億資料出現最多的數以及出現次數

package org.example.bigdata; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List

【面試必備】如何在10億數1000大的數?

作者:channingbreeze | 微信公眾號:網際網路偵察小史是一個應屆生,雖然學的是電子

二叉搜尋樹K大的最小的那個數

這是一道面試的演算法題,題目不難,但是在面試的時候是要求直接在白板上寫程式,感覺自己還是弱爆了。第一次體驗這種面試方式,雖然被虐,但是感覺還是挺好的,這方面的經驗還是需要積累。 題目: 在一個二叉搜尋樹種查詢比某個數(K)大數中最小的那個。 先把二叉樹相關的

海量資料位數c語言實現

題目:5億個int,從中找出第k大的數 演算法:之後補上。。。 實現: #include <assert.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h&g

面試突擊 | Redis 如何從海量資料查詢某一個 Key?附視訊

1 考察知識點 本題考察的知識點有以下幾個: Keys 和 Scan 的區別 Keys 查詢的缺點 Scan 如何使用? Scan 查詢的特點 2 解答思路 Keys 查詢存在的問題 Scan 的使用 Scan 的特點 3 Keys 使用相關 1)Keys 用法如下 2)Keys 存在的問題

一組連續的資料,打亂次序後,隨機取出某一個數字,用最簡單的方法查取出的數字求解

1、對於一組連續的資料,打亂次序後,隨機取出某一個數字(取出數字後,該位置後的所有數字位置前進一位),用最簡單的方法查找出取出的數字。 2、對1的擴充套件,當取出多個數據後,用最簡單的方法查找出取出的數字。 本人開始對於1的情況,將這種場景抽象成通用的模型,跳進了思維陷阱,把2倒是想出來了,暈了....

28、最小的k個數TopK

文章目錄 題目描述: 解題思路: 1、完全排序O(n*logn) 2、部分選擇排序O(n*k) 3、最大堆O(nlogk) 4、基於快排的演算法O(n) 題目描述

典型的Top K演算法 _找出一個數組裡面前K個最大數_找出1億個浮點數最大的10000個_一個文字檔案,找出前10個經常出現的詞,但這次檔案比較長,說是上億行或十億行,總之無法一次讀入記憶體.

        搜尋引擎會通過日誌檔案把使用者每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255位元組。         假設目前有一千萬個記錄(這些查詢串的重複度比較高,雖然總數是1千萬,但如果除去重複後,不超過3百萬個。一個查詢串的重複度越高,說明查詢

100億個數最大的K個數海量TopK問題

對於這個問題,可以有以下思考: 給了多少記憶體儲存這100億個資料? 先思考:堆排序效率是nlogn,再思考:可以將這些資料切成等份,再從每一份中找出最大前k個數據,但是效率不高。那如果利用堆的性質呢? 小堆堆頂元素最小,先將前k個數建成小堆,那麼堆頂元素

海量資料出現次數TOPK的記錄

題目:在一個文字中有大量的字串記錄,統計出現次數最多的字串及其次數。 思路:使用STL中的map可以快速的解決這個問題,map是一類關聯式容器,通過RB樹實現的,自動建立key-value的對應,key和value可以是任何型別。 #include <iostrea

無序數組最大的兩個K

數組;查找;最大;K個**給你一個整型數組,我想找出來最大的兩個數,能幫我寫一個算法嗎? **在上一遍我們已經解讀過這道題目了,包括我們能想到的問題。這裏我們按照解決算法題的一般步驟再來一起分析一下這道題: 一、確保我們理解了問題,並且嘗試一個例子,確認理解無誤。 那現在我們澄清一下問題,我們需要從這樣的數