日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构--位图 BitMap

發布時間:2024/7/5 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构--位图 BitMap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. 位圖
    • 2. 位圖代碼
    • 3. 布隆過濾器 Bloom Filter
    • 4. 總結

1. 位圖

我們有1千萬個整數,整數的范圍在1到1億之間。如何快速查找某個整數是否在這1千萬個整數中呢?

  • 當然,這個問題可以用散列表來解決。可以使用一種特殊的散列表,那就是位圖
  • 申請一個大小為1億、布爾類型(true或者false)的數組。將這1千萬個整數作為數組下標,將對應的數組值設置成true。比如,整數5對應下標為5的數組值設置為true,也就是array[5]=true。
  • 查詢某個整數K是否在這1千萬個整數中的時候,只需將array[K]取出來,看是否等于true。如果等于true,那說明1千萬整數中包含這個整數K;相反,就表示不包含這個整數K。
  • 不過,很多語言中提供的布爾類型,大小是1個字節的,并不能節省太多內存空間。實際上,表示true和false,只需要**一個二進制位(bit)**就可以了。
  • 我們可以借助編程語言中提供的數據類型,比如int、long、char等類型,通過位運算,用其中的某個位表示某個數字。

2. 位圖代碼

#include <iostream> #include <cstring> using namespace std; class BitMap {char *bytes; //char是1字節,8位int nbits; public:BitMap(int n){nbits = n;bytes = new char [nbits/8 + 1];memset(bytes, 0, (nbits/8+1)*sizeof(char));}~BitMap(){delete [] bytes;}void set(int k){if(k > nbits)return;int byteIndex = k/8;int bitIndex = k%8;bytes[byteIndex] |= (1<<bitIndex);}bool get(int k){if(k > nbits)return false;int byteIndex = k/8;int bitIndex = k%8;return (bytes[byteIndex] & (1 << bitIndex)) != 0;}void print(){for(int i = 15; i >= 0; --i)cout << get(i) << " ";} }; int main() {BitMap bm(8);bm.set(8);cout << bm.get(8) << endl;bm.print();return 0; }

比如上面例子,如果用散列表存儲這1千萬的數據,數據是32位的整型數,也就是需要4個字節的存儲空間,那總共至少需要40MB的存儲空間。如果通過位圖的話,數字范圍在1到1億之間,只需要1億個二進制位,1億/8/1024/1024 = 12, 也就是12MB左右的存儲空間就夠了。

不過,這里我們有個假設,就是數字范圍不是很大。如果數字的范圍很大,數字范圍不是1到1億,而是1到10億,那位圖的大小就是10億個二進制位,也就是120MB的大小,消耗的內存空間不降反增

怎么辦?請布隆過濾器登場!

3. 布隆過濾器 Bloom Filter

  • 布隆過濾器就是為了解決剛剛這個問題,對位圖這種數據結構的一種改進。

還是剛剛那個例子,數據個數是1千萬,數據的范圍是1到10億。

  • 布隆過濾器的做法是,我們仍然使用一個1億個二進制大小的位圖,然后通過哈希函數,對數字進行處理,讓它落在這1到1億范圍內。比如我們把哈希函數設計成f(x) = x%n。其中,x表示數字,n表示位圖的大小(1億),也就是,對數字跟位圖的大小進行取模求余。

  • 哈希函數會存在沖突的問題,為了降低沖突概率,可以設計一個復雜點、隨機點的哈希函數。除此之外,還有其他方法嗎?

  • 我們來看布隆過濾器的處理方法。既然一個哈希函數可能會存在沖突,那用多個哈希函數一起定位一個數據,是否能降低沖突的概率呢?

  • 使用 K 個哈希函數,對同一個數字進行求哈希值,那會得到K個不同的哈希值,我們分別記作X1,X2,X3,……Xk 。我們把這 K 個數字作為位圖中的下標,將對應的BitMap[X1],BitMap[X2],BitMap[X3],……BitMap[Xk]都設置成true,也就是說,我們用 K 個二進制位,來表示一個數字的存在。

  • 當我們要查詢某個數字是否存在的時候,我們用同樣的 K 個哈希函數,對這個數字求哈希值,分別得到Y1,Y2,Y3,……Yk 。看這 K 個哈希值,對應位圖中的數值是否都為true,都是true,這個數字存在,任意一個不為true,說明這個數字不存在。

    對于兩個不同的數字,經過 K 個哈希函數處理之后,K 個哈希值都相同的概率就非常低了。盡管采用 K 個哈希函數之后,兩個數字哈希沖突的概率降低了,但是,這種處理方式又帶來了新的問題,那就是容易誤判。看下面例子。

  • 布隆過濾器的誤判有一個特點,那就是,它只會對存在的情況有誤判

  • 如果某個數字經過布隆過濾器判斷不存在,那說明這個數字真的不存在,不會誤判

  • 如果某個數字經過布隆過濾器判斷存在,有可能誤判,有可能并不存在。不過,只要我們調整哈希函數的個數、位圖大小跟要存儲數字的個數之間的比例,那就可以將這種誤判的概率降到非常低。

  • 盡管布隆過濾器會存在誤判,但是,這并不影響它發揮大作用。很多場景對誤判有一定的容忍度

4. 總結

布隆過濾器非常適合這種不需要100%準確的允許存在小概率誤判的大規模判重場景。比如統計一個大型網站的每天的UV數,也就是每天有多少用戶訪問了網站,就可以使用布隆過濾器,對重復訪問的用戶,進行去重。

布隆過濾器的誤判率,主要跟哈希函數的個數位圖的大小有關。往布隆過濾器中不停地加入數據之后,位圖中不是true的位置就越來越少了,誤判率就越來越高了。所以,對于無法事先知道要判重的數據個數的情況,我們需要支持自動擴容的功能。

當布隆過濾器中,數據個數與位圖大小比例超過某個閾值的時候,我們就重新申請一個新的位圖。后面來的新數據,會被放置到新的位圖中。但是,如果我們要判斷某個數據是否在布隆過濾器中已經存在,我們就需要查看多個位圖,相應的執行效率就降低了一些。

位圖、布隆過濾器應用如此廣泛,很多編程語言都已經實現了。比如 Java 中的 BitSet 類就是一個位圖,Redis 也提供了 BitMap 位圖類,Google 的 Guava 工具包提供了BloomFilter 布隆過濾器的實現。

課后思考
1.假設我們有1億個整數,數據范圍是從1到10億,如何快速并且省內存地給這1億個數據從小到大排序?

傳統做法:1億個整數,存儲需要400M空間
位圖算法:數字范圍是1到10億,用位圖存儲125M就夠了,然后將1億個數字依次添加到位圖中,再將位圖按下標從小到大輸出值為1的下標,排序就完成了,時間復雜度為O(n)

總結

以上是生活随笔為你收集整理的数据结构--位图 BitMap的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。