1. 程式人生 > >大資料量獲取TopK的幾種方案

大資料量獲取TopK的幾種方案

一:介紹

    生活中經常會遇到求TopK的問題,在小資料量的情況下可以先將所有資料排序,最後進行遍歷。但是在大資料量情況下,這種的時間複雜度最低的也就是O(NlogN)此處的N可能為10億這麼大的數字,時間複雜度過高,那麼什麼方法可以減少時間複雜度呢,以下幾種方式,與大家分享。

二:區域性淘汰法 -- 藉助“氣泡排序”獲取TopK

  1. 思路:

    • 可以避免對所有資料進行排序,只排序部分

    • 氣泡排序是每一輪排序都會獲得一個最大值,則K輪排序即可獲得TopK

  2. 時間複雜度空間複雜度

    • 時間複雜度:排序一輪是O(N),則K次排序總時間複雜度為:O(KN)

  3. 程式碼比較簡單就不貼了,只要會寫冒泡就ok了

三:區域性淘汰法 -- 藉助資料結構"堆"獲取TopK

  1. 思路:

    • 堆:分為大頂堆(堆頂元素大於其他所有元素)和小頂堆(堆頂其他元素小於所有其他元素)

    • 我們使用小頂堆來實現,為什麼不適用大頂堆下面會介紹~

    • 然後迴圈從K下標位置遍歷資料,只要元素大於堆頂,我們就將堆頂賦值為該元素,然後重新調整為小頂堆

    • 迴圈完畢後,K個元素的堆陣列就是我們所需要的TopK

  2. 為什麼使用小頂堆呢?

    • 我們在比較的過程中使用堆頂是最小值的小頂堆,元素大於堆頂我們對堆頂進行重新賦值,那麼堆頂永遠是這K個值中最小的值,當我們下一個元素和堆頂比較時,如果不大於堆頂的話,那麼一定不屬於topK範圍的

  3. 時間複雜度與空間複雜度

    • 時間複雜度:每次對K個元素進行建堆,時間複雜度為:O(KlogK),加上N-K次的迴圈,則總時間複雜度為O((K+(N-K))logK),即O(NlogK),其中K為想要獲取的TopK的數量N為總資料量

    • 空間複雜度:O(K),只需要新建一個K大小的陣列用來儲存topK即可

  4. 適用環境

    • 適用於單核單機環境,不會發揮多核的優勢

    • 也可用於分治法中獲取每一份元素的Top,下面會介紹

  5. 程式碼實現

    • 使用的java程式碼實現的,程式碼內每一步都有註釋便於理解

import java.util.Arrays;

/**
* 通過堆這種資料結構
* 獲得大資料量中的TopK
*/

public class TopKStack {
    public static void main(String[] args) {
        //定義一個數組,找出該陣列中的topK,大資料量不好搞到,先用這個陣列測試
        int [] datas = {2,3,42,1,34,5,6,67,3,243,8,246,123,6,32,3451,23,5,6,31,5,6,2346,36};
        int [] re = getTopK(datas,10);
        System.out.println(Arrays.toString(re));
    }

    /**
    * 獲取前topk的方法
    * @param datas 原陣列
    * @param num 前topNum
    * @return 最後的topNum的堆陣列
    */
    static int[] getTopK(int[] datas,int num){
        //定義儲存前num個元素的陣列,用於建堆
        int[] res = new int[num];
        //初始化陣列
        for (int i = 0; i < num; i++) {
            res[i] = datas[i];
        }
        //建造初始化堆
        for (int i = (num - 1)/2; i >= 0 ; i--) {
            shift(res,i);
        }
        //遍歷查詢num個最大值
        for (int i = num; i < datas.length; i++) {
            if (datas[i] > res[0]){
                res[0] = datas[i];
                shift(res,0);
           }
        }
        return res;
    }

    /**
    * 調整元素滿足堆結構
    * @param datas
    * @param index
    * @return
    */
    static int[] shift(int[] datas ,int index){
        while(true){
            int left = (index<<1) + 1; //左孩子
            int right = (index<<1) + 2; //右孩子

            int min_num = index; //標識自身節點和孩子節點中最小值的位置
            //判斷是否存在左右孩子,並且得到左右孩子和自身的最小值
            if (left <= datas.length-1&&datas[left] < datas[index]){
                min_num = left;
            }
            if (right <= datas.length-1&&datas[right] < datas[min_num]){
                min_num = right;
        }
        //如果最小值不等於自身,則將最小值與自身交換
        if (min_num != index){
            int temp = datas[index];
            datas[index] = datas[min_num];
            datas[min_num] = temp;
        }else{
            //此處break是因為我們是從樹的最下面進行調整的,如果上層節點符合堆,則下層節點一定符合!
            break;
        }

        //執行到此處,說明可能需要調整下面的節點,則將初始節點賦值為最小值所在的節點位置,
        // 因為最大值點的位置進行了交換,可能下層節點就不滿足堆性質
        index = min_num;
    }
    return datas;
   }
}

四:分治法 -- 藉助”快速排序“方法獲取TopK

  1. 思路:

    • 比如有10億的資料,找處Top1000,我們先將10億的資料分成1000份,每份100萬條資料

    • 在每一份中找出對應的Top 1000,整合到一個數組中,得到100萬條資料,這樣過濾掉了999%%的資料

    • 使用快速排序對這100萬條資料進行”一輪“排序,一輪排序之後指標的位置指向的數字假設為S,會將陣列分為兩部分,一部分大於S記作Si,一部分小於S記作Sj。 ps:快速排序請參考:https://blog.csdn.net/CSDN___LYY/article/details/81478583

    • 如果Si元素個數大於1000,我們對Si陣列再進行一輪排序,再次將Si分成了Si和Sj。如果Si的元素小於1000,則我們需要在Sj中獲取1000-count(Si)個元素的,也就是對Sj進行排序

    • 如此遞迴下去即可獲得TopK

  2. 和第一種方法有什麼不同呢?相對來說的優點是什麼?

    • 第二種方法中我們可以採用多核的優勢,建立多個執行緒,分別去操作不同的資料。

    • 當然我們在分治的第二步可以使用第一種方法去獲取每一份的Top。

  3. 適用環境

    • 多核多機的情況,分治法會將多核的作用發揮到最大,節省大量時間

  4. 時間複雜度與空間複雜度

    • 時間複雜度:一份獲取前TopK的時間複雜度:O((N/n)logK)。則所有份數為:O(NlogK),但是分治法我們會使用多核多機的資源,比如我們有S個執行緒同時處理。則時間複雜度為:O((N/S)logK)。之後進行快排序,一次的時間複雜度為:O(N),假設排序了M次之後得到結果,則時間複雜度為:O(MN)。所以 ,總時間複雜度大約為O(MN+(N/S)logK) 。

    • 空間複雜度:需要每一份一個數組,則空間複雜度為O(N)

五:其他情況

  • 通常我們要根據資料的情況去判斷我們使用什麼方法,在獲取TopK前我們可以做什麼操作減少資料量。

  • 比如:資料集中有許多重複的資料並且我們需要的是前TopK個不同的數,我們可以先進行去重之後再獲取前TopK。如何進行大資料量的去重操作呢,簡單的說一下:

    1. 採用bitmap來進行去重。

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

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

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

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

  • 主要還是根據記憶體、核數、最大建立執行緒數來動態判斷如何獲取前TopK。

相關推薦

料量獲取TopK方案

一:介紹     生活中經常會遇到求TopK的問題,在小資料量的情況下可以先將所有資料排序,最後進行遍歷。但是在大資料量情況下,這種的時間複雜度最低的也就是O(NlogN)此處的N可能為10億這麼大的數字,時間複雜度過高,那麼什麼方法可以減少時間複雜度呢,以下幾種方式,與大

EditText 自動搜尋本地資料庫(料量)卡頓解決方案

假設本地存了很多資料,按關鍵字搜尋,而且要求自動搜尋,沒有搜尋按鈕,輸入法上也沒有,就要求這種體驗,當你輸入一個字元的時候,EditText的addTextChangedListener其實就開始監聽了,比如你想搜尋abc ,其實查詢了三次資料庫,先搜a,再ab,然後才是

基於料量的快取查詢實現方案

       業務、應用系統最常用的就是基於資料的查詢,這不同於巨集觀意義上的系統各個層面優化(應用端、服務端、DB端等等),基於資料的查詢更多時候需要考慮資料的規模、使用者的習慣、資料的變化性等因素

料量,高併發解決方案

解決大資料量高併發要考慮多方面的1.HTML靜態化2.靜態檔案伺服器分離  如圖片、css、js檔案等;3.資料庫叢集4.負載均衡5.快取6.讀寫分離Discuz!NT資料庫讀寫分離方案,我感覺這個還不錯http://www.cnblogs.com/daizhj/archiv

料量下查詢顯示優化方案小結

# 大資料量下查詢顯示優化方案小結 # 最近工作中,遇到了優化大批量資料查詢和顯示的問題,資料量在10W級別。經過反覆設計和討論,最終得到優化到了較為滿意的效果,在此記錄小結下,在解決此類問題中的思考。 ## 問題背景說明 ## 通常情況下,使用者查詢資料量不超過1千條,但有幾個大戶,通過某種方式,生成

料量方案收集--AdMaster 如何駕馭百億級Key實時Redis 叢集

注:本文轉載自公眾號AdMaster 作為技術驅動的營銷資料公司,AdMaster每天處理超過100億的資料請求,每天對1000億資料進行上千種維度計算,每天增加超過5T資料量,為來自各行業的客戶提供7*24小時資料應用服務。在這樣領先的技術佈局下,無論是資料實時性還是資料安全,都能得到

快排與兩歸併和堆和插入排序 料量執行時間比較

#include"iostream" #include"iomanip" #include"stdlib.h" #include"time.h" #include"string" /*由於我電腦記憶體有限所以資料量最大能執行在20w*/ //三路快排適用於有大量重複值的資

料量情況下查詢效能低,耗時長的一問題以及解決思路

背景交代: 1   mongodb 有500萬條資料                  2  經過過濾 還有20多萬條資料 要得到上述20w條資料,一次查詢得到20多萬條,很可能會產生效能問題,於

料量同步方案之全量同步改為增量同步解決方案

背景描述:   在一些大資料運用場景中,由於上游資料每天都在變化著,在需要用這些資料的下游系統需要每天重新整理這些變化的資料,當資料量小時候,簡單粗暴的方式就是每次全量更新資料,但隨著業務的增長,資料量成幾何方式增長時(達到億級別甚至更多),每次的更新工作將是

料量、高併發量網站解決方案

      一個小型的網站,可以使用最簡單的html靜態頁面就實現了,配合一些圖片達到美化效果,所有的頁面均存放在一個目錄下,這樣的網站對系統架構、效能的要求都很簡單。隨著網際網路業務的不斷豐富,網站相關的技術經過這些年的發展,已經細分到很細的方方面面,尤其對於大型網站來說

Redis料量(百億級)Key儲存需求及解決方案

問題導讀: 1. 需求背景是什麼? 2. 儲存何種資料? 3. 資料特點是什麼? 4. 存在哪些技術挑戰? 5. 解決方案有哪些? 6. md5雜湊桶的方法需要注意哪些問題? 7. 測試結果是什麼? 解決方案: 1 需求背景     該應用場景為 DM

.Net中EF針對料量查詢超時的一優化

舊程式碼:--receiptIds   id集合,每次查1000左右var mappingList = new List<FinanceSettlementMapping>();mappingList.AddRange(SettlementMappingRepos

MySQL 料量表優化方案

單表優化 除非單表資料未來會一直不斷上漲(例如網路爬蟲),否則不要一開始就考慮拆分,拆分會帶來邏輯、部署、運維的各種複雜度 一般以整型值為主的表在 千萬級以下,字串為主的表在 五百萬以下是沒有太大問題的。而事實上很多時候 MySQL 單表的效能依然有不少優化空間,甚至能正

存在彙總統計等功能的料量報表的優化方案

對於大資料量,多層分組的的彙總報表不能採用分頁標籤,可以採用如下的優化方案: 一、先初步對報表進行優化: 1、儘量在sql實現group分組,資料庫雖然要進行分組運算,但是資料庫中有索引,運算速度快,且 取到報表伺服器端的記錄數大大減少,取數速度大大加快,因此在報表端進行分

.NET 料量併發解決方案

大併發大資料量請求一般會分為幾種情況:大量的使用者同時對系統的不同功能頁面進行查詢、更新操作大量的使用者同時對系統的同一個頁面,同一個表的大資料量進行查詢操作大量的使用者同時對系統的同一個頁面,同一個表進行更新操作第一類情況 :大量的使用者同時對系統的不同功能頁面進行查詢、更

Spring 獲取bean 方式

讀取 獲取 static ava ade beans java ride .html 轉載自: http://www.cnblogs.com/luoluoshidafu/p/5659574.html 1.讀取xml文件的方式,這種在初學入門的時候比較適用 。     A

Spring3 MVC請求參數獲取方法

setup return 異常 pathvaria method let 方法 ces 解決 一、 [email protected]/* */ @RequestMapping(value="user/{id}/{name}",method=Requ

web端文字轉語音的方案

網站 文字轉語音 rate str source req 實現 mes contex 最近在開發一個微信排隊取號的的系統,其中對於服務員端(管理端) 需要有呼叫功能,即點按鈕 就播出"xxx號顧客請就座"的聲音。 經過在網上一番搜索研究,web端實現指定文字的語音播放 方案

springMVC中處理靜態資源的方案

image handle source 資源 ima -m web.xml 配置文件 resource 處理靜態資源方案一:在web.xml文件中配置如下: <!-- &lt;!&ndash;解決靜態資源方案&ndash;&gt; &

建築物高度數據的獲取方法

高度 工作量 數據 影像 其他 自動 數據庫 缺點 建築 1)從影像中直接提取建築物高度以及其他信息。其優點是效率高,但是目前還不適合大批量數據的自動處理。 (2)用激光雷達結合空中影像,提取數字表面模型。其優點是獲取速度快,缺點是後續處理工作量大,費用可觀。 (3)利用原