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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Algorithms_算法专项_Bitmap原理及应用

發布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Algorithms_算法专项_Bitmap原理及应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 引導案例
  • 基礎知識回顧
  • Bitmap
    • 計算不同存儲類型需要開辟的內存空間
    • 原理
      • 計算需要開辟多大長度的數組
      • 找到目標數字在數組中的下標
      • 找到目標數字在對應數組下標中的位置
      • 總結下3個步驟
  • 判斷是否存在
  • 刪除
  • 代碼
  • 測試
  • bitmap優缺點總結
    • 優點
    • 缺點
  • 小試牛刀

引導案例

Q: 如何在3億個整數, 每個整數的范圍是 0到2億,判斷一個數是否存在于3億個整數中。 要求內存使用在100M以內,一臺主機。

思考下…

數組? hash ? 分治? bitmap ? 布隆過濾器 ?


用數組的話 , 開辟個data[2億]長度的數組, data[1]=1 表示存在,data[2]=0表示不存在。 理論上可行,但是內存肯定超過了500M, 不可行

用hash的話,開3億個空間, 舉個例子 使用HashMap --> put(1,true) , get(1)為true,則存在, 聽起來也可行哈,但是3億個int類型 內存也是絕對會超過500M的。

分治: 內存要求也不滿足

bitmap ? 布隆過濾器? 這兩個是可以的

我們這里先用bitmap來解決這個問題


基礎知識回顧

Algorithms_數據結構與算法_位運算符的巧技一二事


Bitmap

計算不同存儲類型需要開辟的內存空間

在計算機科學中,bit通常用于表示信息的最小單位,也可以被稱作是二進制位。

在計算機學科中,bit一般用0和1表示

Byte也就是人們常說的字節,通常由8個位(8bit)組成一個字節(1Byte)

比如我們常見的基本類型的取值范圍

bit與Byte之間可以進行換算,具體的換算關系為:1Byte=8bit

計算下3億數據需要開辟的內存空間:

  • 如果使用 int數組來存儲, 那就需要3億個int , 1個int占用4個Byte字節 , 故3億個int 占用的內存空間為 3億 * 4 (總共占用的Byte) / 1024 (轉為KB) / 1024 (轉為MB) = 1144 MB

  • 如果使用 bit數組來存儲,那么同樣也需要3億個bit, 8個bit才等于 1個Byte字節, 故3億個bit 占用內存空間為 3億/8 (總共占用的Byte) / 1024 (轉為KB) / 1024 (轉為MB) = 35.7 MB

  • 當然了 你也可以用long 或者 short來存儲,無非就是計算的時候的轉換關系調整下 。 如果使用 long 數組來存儲, 那就需要3億個long , 1個long 占用8個Byte字節 , 故3億個long占用的內存空間為 3億 * 8 (總共占用的Byte) / 1024 (轉為KB) / 1024 (轉為MB) = 2288 MB 。

  • 如果使用 short數組來存儲, 那就需要3億個short, 1個short占用2個Byte字節 , 故3億個short占用的內存空間為 3億 * 2(總共占用的Byte) / 1024 (轉為KB) / 1024 (轉為MB) = 572MB 。

  • 其他類型也可以,主要是需要開辟的內存大小,當然是越小越好了…

結論顯而易見,使用二進制bit來存儲 3億個數,比使用int來存儲3億個數 需要開辟的內存空間從1個G 降低為 36M, 少了30倍 ,你說它不香嗎???


原理

bitmap就是利用一個 bit 二進制位(0,1)來存儲。由于采用bit為單位來存儲數據,可以大大的節省存儲空間

OK , 計算完了需要開辟的內存空間,如果使用bit來存儲這些數據,該如何表示呢?

我們知道bit 二進制位,在計算機學科中,bit一般用0和1表示

每一個bit位表示一個數,0表示不存在,1表示存在,這正符合bit二進制。 舉個例子我們要存儲 {1,2,4,6}

我們需要用bit來 存儲,java.util包中有個專門存儲bit的類 BitSet

當然了,我們也可以用其他類型來表示bit . 接下來我們以int為例 (當然了 你用byte , short ,long 都可以…)


計算需要開辟多大長度的數組

比如我們使用int來表示bit , 1個int = 4 個Byte = 4 * 8 bit . 因此一個int可以存儲32個int

假設有 65個數字(0~64),最大數字為64 。 用bit表示,肯定需要64bit ,如果用int來表示,需要幾個int呢 ?

我們來計算下

-----> 需要3個int,才能把這65個bit 存下來。

-----> 65個數字 需要3個int (64/32 +1) , 97個數字 需要4個int (96/32+1) … 【從0開始計算,最大值 64 ,96】

-----> 推到出來一個公式來根據存儲的bit數量計算(取數據的最大值)需要幾個int

故需要開辟的int數組的容量為 (MAX/32 + 1) , 一定要注意的是 一定要取MAX來計算。 至于為啥子除以32 , 1 int = 4 Byte = 4 * 8 bit ,通過轉換計算來的。 看到MAX/32 這種 ,應該馬上想到 右移 2^5次方 這種高效的計算方式。 即 (MAX >> 5 + 1 )


找到目標數字在數組中的下標

確定了需要開辟幾個int數組,假設我們要把64這個值存儲到bit中(當然了,存到bit里肯定不是64 ,bit二進制位 存0 1。 1 表示存在,0 表示不存在)

是不是得找到這個具體的位置 ?

----> 是不是需要先定位到數組中的哪個int , 比如 64 在 int[2],先找到 int[2]

----> 64~95 在 int[2] , 32~63 在int[1] , 96-127 在int[4]

—> 推到出來一個公式來 找到目標數字在數組中的下標 n / 32 ,同樣的 ,通過位移的方式更高效 n/32 —> n/2^5 --> n >> 5


找到目標數字在對應數組下標中的位置

----> 然后再看下 這個int存儲了32位,那我這個目標值應該在第幾個位置 ,顯而易見,我們的64在第一個位置。

----> 64 在 int[2] 的 第1位,即下標為0的位置, 65呢 下標為1的位置 ,70 呢 下標為6

—> 推到出來一個公式來 找到目標數字在在對應數組下標中的位置 ,取余即可 n %32 ,(因為除數32位2^5)所以 ,通過位移的方式更高效 n%32 —> n%2^5 --> n &(2^5-1) —> n & 31

除數為2的n次方時,使用位操作(&運算)代替求余操作


總結下3個步驟

1. 計算需要開辟多大長度的數組 :(MAX >> 5 + 1 )
2. 找到目標數字在數組中的下標 : n >> 5
3. 找到目標數字在對應數組下標中的位置 : n & 31

注意: 計算需要開辟多大長度的數組,一定要取最大值計算,假設你要存3億個數據
—> 根據公式 需要開辟的數組長度為: 3億/32 + 1 = 9375001(9百30多萬) 。

(1個int 占4個字節,9375001 * 4 (總字節大小) /1024 /1024 = 35.76MB)


存的本質就是將下標為pos的位由0變為1---->那就把1左移pos位,然后使用或運算符運算即可 (按位或運算【|】有一個為1時,結果位就為1)

舉個例子 pos = 2 , pos 2原本的bit值是 0 (其實不管是0 ,還是1)

–> 0 | 1 --> 1
–> 1 | 1 --> 1

/*** 初始化數據** 其實用啥和1進行或運算都行,這里使用bitsArray[index(n)]* @return*/public boolean add(int n) {// 或運算return (bitsArray[index(n)] |= 1 << position(n)) != 0;}

判斷是否存在

public boolean find(int n) {// bitMap構建的時候,根據max來構建的,如果入參n>max,直接返回不存在if (n > max) return false;return (bitsArray[index(n)] &= 1<<position(n)) != 0;}

假設黃色的二進制位1存儲的是數字n,要想判斷n是否存在
第一步: 找到n所在的數組下標
第二步: 在第一步確定的那個數組下標對應的int里(假設我們是用int來存儲bit,32個bit), 確定這個n具體在哪個position,
第三步:把1 左移 position個位置,然后和目標的二進制bit位,進行 與運算. 只有都為1,才說明存在。 如果目標的二進制bit位是0,0&1=0 ,說明這個位置沒有被add過,也就不存在。

注: add的時候,用的是bitsArray[index(n)] |= 1 << position(n) ,所以add完成以后bitsArray[index(n)]就有值了,所以這里繼續使用bitsArray[index(n)]進行 &運算


刪除

刪除無非就是把 1置為 0 。

將1左移position后,因為存在,那個位置自然就是1,然后取反就是0,再與當前值做&,即可清除當前的位置了

/*** * @param n* @return*/public boolean del(int n) {// 不存在 返回刪除失敗if (!find(n)) return false;// 將1左移position后,因為存在,那個位置自然就是1,然后取反就是0,// 再與當前值做&,即可清除當前的位置了.return (bitsArray[index(n)] &= ~(1<<position(n))) == 0 ;}

代碼

/*** @author 小工匠* @version v1.0* @create 2019-12-20 6:47* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class ArtisanBitMap {// 這批數據的最大值,用于確定需要開辟的數組長度private int max;// 數據映射到bit位上,這里用int表示的bit ,1個int占4個字節即4*1Byte = 4*8bit = 32bitprivate int[] bitsArray;/*** 構造函數** @param max*/public ArtisanBitMap(int max) {this.max = max;// 構建int數組,用于存儲bit// (max >> 5) 一定要加括號,因為 +號的優先級比>>高bitsArray = new int[(max >> 5) + 1];}/*** 初始化數據** 其實用啥和1進行或運算都行,這里使用bitsArray[index(n)]* @return*/public boolean add(int n) {// 或運算return (bitsArray[index(n)] |= 1 << position(n)) != 0;}public boolean find(int n) {// bitMap構建的時候,根據max來構建的,如果入參n>max,直接返回不存在if (n > max) return false;return (bitsArray[index(n)] &= 1<<position(n)) != 0;}/**** @param n* @return*/public boolean del(int n) {// 不存在 返回刪除失敗if (!find(n)) return false;// 將1左移position后,因為存在,那個位置自然就是1,然后取反就是0,// 再與當前值做&,即可清除當前的位置了.return (bitsArray[index(n)] &= ~(1<<position(n))) == 0 ;}/*** 根據n 判斷n所在的數組下標** @param n* @return*/public int index(int n) {// n/32 --> n 除以 32 (2^5) ,可以表示為 n向右移5位return n >> 5;}/*** 根據n 判斷n所在數組下標對應的存儲位置* 即:數組中的一個int可以存儲32位,判斷這個n需要存儲在哪一位** @param n* @return*/public int position(int n) {// n%32 --> n 取余32 (2^5) ,可以表示為 n &(2^5 -1)return n & 31;}public static void main(String[] args) {// 最大是65ArtisanBitMap bitMap = new ArtisanBitMap(300000000);bitMap.add(2);bitMap.add(300000000);// 判斷是否存在boolean a = bitMap.find(2);System.out.println("2是否存在: " + a);boolean b = bitMap.find(300000000);System.out.println("300000000是否存在: " + b);// 刪除操作boolean delete = bitMap.del(2);System.out.println("2是否刪除成功: " + delete);System.out.println("2是否存在: " + bitMap.find(2));}}

測試



bitmap優缺點總結

優點

  • BitMap的時間復雜度是O(1), 高效
  • 占用內存少 ,空間復雜度

  • 缺點

  • 數據最好不要重復。因為只用0和1表示 , 如果重復,無法知道這個數字有多少個,只能知道這個數字存在。 比如有3個6666,每一次add都能成功,但是無法知道有幾個6666. 如果你的需求是統計存不存在,那可以重復,你如果要統計某個數字出現的次數…
  • 無法處理字符串 。 將字符串映射到 BitMap 的時候會有碰撞的問題,可以考慮用 Bloom Filter 來解決,Bloom Filter 使用多個 Hash 函數來減少沖突的概率。
  • 數據稀疏 。 數據量少的情況下相對于普通的hash存儲并沒有太大的優勢,舉個例子 比如要存入(3,9877899877,98721124)這三個數據,我們需要建立一個 9999999999 長度的 BitMap ,但是實際上只存了3個數據,這時候就有很大的空間浪費,碰到這種問題的話,普通的hash或者 Roaring BitMap 來解決。
  • 所以針對存在的缺點,就要布隆過濾器這個神器了,下篇我們探討下布隆過濾器的原理及使用。


    小試牛刀

    Q: 某個超大的文件中包含一些手機號碼,每個號碼為11位數字,求不同號碼的個數。

    有了上面的處理思路,是不是這個問題也有思路了呢?

    每個號碼11位, 最大值也就是 11個9唄…so , 懂了吧

    總結

    以上是生活随笔為你收集整理的Algorithms_算法专项_Bitmap原理及应用的全部內容,希望文章能夠幫你解決所遇到的問題。

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