bitmap計數,求TopK最快的方法

2021-08-28 17:14:00 字數 2291 閱讀 2987

上篇介紹了topk的四種解法,其中隨機選擇(randomized select) 最為經典,用減治法(reduce & conquer) 的思想,將資料規模急速降低,總體複雜度為o(n)。

結尾挖了乙個坑:求topk,有沒有比隨機選擇更快的方法呢?

空間換時間,是演算法優化中最常見的手段,如果有相對充裕的記憶體,可以有更快的演算法。

畫外音:即使記憶體不夠,也可以水平切分,使用分段的方法來操作,減少每次記憶體使用量。

topk問題描述

從arr[1, 12]= 這n=12個數中,找出最大的k=5個。

位元位圖(bitmap)

bitmap,是空間換時間的典型代表。它是一種,用若干個bit來表示集合的資料結構。

例如,集合s=,容易發現,s中所有元素都在1-16之間,於是,可以用16個bit來表示這個集合:存在於集合中的元素,對應bit置1,否則置0。

畫外音:究竟需要多少存儲存空間,取決於集合中元素的值域,在什麼範圍之內。

上述集合s,可以用1010101010000000這樣乙個16bit的bitmap來表示,其中,第1, 3, 5, 7, 9個bit位置是1。

假設topk的n個元素都是int,且元素之間沒有重複,只需要申請2^32個bit,即512m的記憶體,就能夠用bitmap表示這n元素。

掃瞄一次所有n個元素,以生成bitmap,其時間複雜度是o(n)。生成後,取topk只需要找到最高位的k個bit即可。演算法總時間複雜度也是o(n)。

偽**為:

bitmap[4g] = make_bitmap(arr[1, n]);

return bitmap[top k bits];

bitmap演算法有個缺點,如果集合元素有重複,相同的元素會被去重,假設集合s中有5個1,最終s製作成bitmap後,這5個1只對應1個bit位,相當於4個元素被丟掉了,這樣會導致,找到的topk不准。該怎麼優化呢?

位元位圖計數

優化方法是,每個元素的1個bit變成1個計數。

如上圖所示,topk的集合經過位元位圖計數處理後,會記錄每個bit對應在集合s中出現過多少次。

接下來,找topk的過程,就是bitmap從高位的計數開始,往低位的計數掃瞄,得到count之和等於k,對應的bit就是topk所求。

如上圖所示,k=5:

(1)第乙個非0的count是1,對應的bit是9;

(2)第二個非0的count也是1,對應的bit是8;

(3)第三個非0的count是2,對應的bit是7;

(4)第四個非0的count是2,對應的bit是6,但topk只缺1個數字了,故只有1個6入選;

故,最終的topk=。

結論:通過位元位圖精準計數的方式,求解topk,演算法整體只需要不到2次掃瞄,時間複雜度為o(n),比減治法的隨機選擇會更快。

為了鞏固今天的內容,例行挖個坑。

面試中,還有個問題問得比較多:求乙個正整數的二進位制表示包含多少個1?

例如:7的二進位制表示是111,即7的二進位制表示包含3個1。

畫外音:我面試過程中從不問這個問題。

最常見的解法是:

求海量資料的topK問題

問題描述 取給定list中的前topk個最大的元素並輸出。關鍵點 1.topk個最大的元素 2.我們並不需要順序,因此一切涉及到sort的工作都是不必要的。3.要且只要topk個元素,no more needed!因此,我們雖然用堆,但並不需要將整個list都建成堆,只需要維護乙個k個元素的堆即可。...

C 小頂堆求Topk

求陣列中的topk數字,比如 1 4 6 7 2 9 8 3 5 0 的top4是 6 7 8 9 用小頂堆來實現,如下,minheap.cpp include include using namespace std class minheap minheap minheap int k 建立小頂堆...

numpy 求TOP k 找出陣列都相同的數

numpy使用的方法 快速從一組資料中找到最大的值 掌握在矩陣中索引的使用方法 import numpy as np z np.arange 10000 b np.random.shuffle z n 5print z np.argpartition z,n n z np.random.randin...