1. 程式人生 > >位示圖演算法實現大資料的儲存

位示圖演算法實現大資料的儲存

          今天在看排序演算法的時候,看到了用位示圖法實現的,上面說可以大大減少記憶體的使用,尤其針對大資料的儲存,資料量非常大的的時候,他的優點就比較明顯了,因為他儲存資料值依靠1個位來表示。具體是怎麼回事呢,繼續往下看。點陣圖法,點陣圖法,當然和位相關,下面我給出一組陣列int[]{1, 3, 5,8},也許在普通的我們的程式設計中,我們肯定是存放在一個32位整形的陣列中,1個32位整數,4個位元組,4個數字總共16個位元組,而如果用點陣圖法,表示,可以用一個這麼一個位字串表示:

10101001

1出現的位置就是代表數字的值,第1,3,5,8個位置的值為1,就代表了這些數字的存在,這樣一算只需要8位,也就是1個位元組,比剛剛那個整整省了15個位元組。可見這個演算法的效率十分之高,但是又一個難題擺在面前了,如何儲存和讀取這樣的形式中的數值無疑將是這個演算法的最難點。


        首先是資料的存入過程,將整形數字存入相應的位,並把相應的位置的數字標記為1,就代表完成任務了,第一步,我們當然得先宣告一個用來存放數值的陣列:

//聲明瞭整形陣列,最大的存放的數字的值可以到3 * 32
	private static int[] bitmap = new int[3];
我在這裡定義了3個單位的陣列,每個陣列值含32位,也就是說,是3個32位0排成一起,那最大表示的值就是96了。這個很好理解吧。為了容易懂,把操作改小了容易懂。接下來就是我們要傳入值進行位設定操作了,主要分為以下步驟:

1.算出是在哪個一個數組下標的範圍內,除以32去商

2.知道了哪個下標了,算出在此下標的偏移位置,再取餘32操作

3.根據偏移量,折算出此時的二進位制形式值,就是一直左移到那個位置上

4.最後進行或運算,把那個位置的值變為1,操作結束

附上程式碼:

/**
	 * 傳入的數字,用來儲存在點陣圖中
	 * @param number
	 */
	public static void setBitmapNumber(int number){
		int index = number / 32;
		int pos = number % 32;
		int flag = 1;
		
		//如果剛好整除
		if(pos == 0){
			//算上個整數位的
			pos = 32;
			index--;
		}
		
		for(int j=0; j<pos-1; j++){
			//左移pos個位置,用於後面的位運算
			flag = flag << 1;
		}
		
		//找到此陣列的位置,進行或運算,把此位置的0變為1
		bitmap[index] = bitmap[index] | flag; 
		System.out.println(MessageFormat.format("第{0}下標,第{1}位,值為{2}", index, pos, bitmap[index]));
	}
我在這裡的測試例子的輸出結果,輸入的數字為:
int[] initArray = new int[]{15, 31, 75};
		for (int i = 0; i < initArray.length; i++) {
			setBitmapNumber(initArray[i]);
		}
輸出為:
第0下標,第15位,值為16,384
第0下標,第31位,值為1,073,758,208
第2下標,第11位,值為1,024
第0下標的意思是這個1的位置在bitmap[0]中,

75因為已經過了2給32,所以在bitmap[2]中,第11位置,十進位制的值就為2的10次方,就是1024了。

0000000000000010000000000000001
0000000000000000000000000000000
0000000000100000000000000000000
上面就是此操作的儲存結果。
/**
	 * 整數表示成二進位制位的形式
	 * @param number
	 */
	public static void setIntToBit(int number){
		int flag = 1;
		for(int j=0; j<31; j++){
			if((number & flag) == 1){
				//說明最右邊一位為1
				System.out.print(1);
			}else{
				System.out.print(0);
			}
			number = number >> 1;
		}
		
		System.out.print("\n");
	}
我寫了這個方法,轉化了一下。就是不斷右移,輸出,迴圈操作。
      當然了,有存入必然有取出的過程,思路主要如下:

1.遍歷biamap[]整形陣列,從0下標到最後一個小標

2.在每個下標中,再執行32位的逐位掃描,判斷有無設定1的存在

3.如果有1的存在,此時的偏移量+下標*32,就是之前下標的總位數,就是最終的值了

程式碼如下:

/**
	 * 獲取點陣圖中所存放的數字
	 */
	private static void getBitmapNumber(){
		int temp = 0;
		//在單個數字中的位偏移量
		int offset = 1;
		int flag = 1;
		
		for(int i=0; i<bitmap.length; i++){
			temp = bitmap[i];
			
			for(int j=0; j<31; j++){
				if((temp & flag) == 1){
					//說明最右邊一位為1
					System.out.println(MessageFormat.format("第{0}下標,第{1}位,值為{2}", i, offset, 32 * i + offset));
				}
				
				temp = temp >> 1;
				offset++;
			}
			//重置偏移量為1
			offset = 1;
		}
	}
測試程式碼輸出的結果:
第0下標,第15位,值為15
第0下標,第31位,值為31
第2下標,第11位,值為75
裡面涉及了比較多的位運算,大家可以自己先執行一下,他到底是怎麼算的。看起來操作都已經成功了嘛,但是,我在除錯程式的時候發現了一個問題,每當我用32的整數倍的值去算的時候,就會失敗,首先存入就會失敗,沒錯,就是越界的問題,後來想了想總共32位,在java裡沒有無符號整形的說法,所以肯定有1位要作為符號位,所以只能表示到31的位置,32位就會出錯,不信的話,可以把我的程式碼拷貝執行,看一下結果。我這個只是一個小小Demo型別的測試,當資料量非常大的時候,你可以用幾個M的位元組,去存放海量的資料,肯定比每個傳統儲存形式要高效很多,而且位運算的執行效率比普通的加減乘除也來的直接。再一次見證了演算法的魅力了吧。最後貼出完整程式供大家學習:
package BitmapStore;

import java.text.MessageFormat;

/**
 * 位示圖法儲存資料,面對大資料的時候可以減少記憶體的使用
 * @author lyq
 *
 */
public class Client {
	//聲明瞭整形陣列,最大的存放的數字的值可以到3 * 32
	private static int[] bitmap = new int[3];
	 
	public static void main(String[] agrs){
	//	int[] initArray = new int[]{2, 15, 33, 75, 178, 1000};
		int[] initArray = new int[]{15, 31, 75};
		for (int i = 0; i < initArray.length; i++) {
			setBitmapNumber(initArray[i]);
		}
		
		for (int i = 0; i < initArray.length; i++) {
			setIntToBit(bitmap[i]);
		} 
		
		getBitmapNumber();
	}
	
	/**
	 * 傳入的數字,用來儲存在點陣圖中
	 * @param number
	 */
	public static void setBitmapNumber(int number){
		int index = number / 32;
		int pos = number % 32;
		int flag = 1;
		
		//如果剛好整除
		if(pos == 0){
			//算上個整數位的
			pos = 32;
			index--;
		}
		
		for(int j=0; j<pos-1; j++){
			//左移pos個位置,用於後面的位運算
			flag = flag << 1;
		}
		
		//找到此陣列的位置,進行或運算,把此位置的0變為1
		bitmap[index] = bitmap[index] | flag; 
		System.out.println(MessageFormat.format("第{0}下標,第{1}位,值為{2}", index, pos, bitmap[index]));
	}
	
	/**
	 * 獲取點陣圖中所存放的數字
	 */
	private static void getBitmapNumber(){
		int temp = 0;
		//在單個數字中的位偏移量
		int offset = 1;
		int flag = 1;
		
		for(int i=0; i<bitmap.length; i++){
			temp = bitmap[i];
			
			for(int j=0; j<31; j++){
				if((temp & flag) == 1){
					//說明最右邊一位為1
					System.out.println(MessageFormat.format("第{0}下標,第{1}位,值為{2}", i, offset, 32 * i + offset));
				}
				
				temp = temp >> 1;
				offset++;
			}
			//重置偏移量為1
			offset = 1;
		}
	}
	
	/**
	 * 整數表示成二進位制位的形式
	 * @param number
	 */
	public static void setIntToBit(int number){
		int flag = 1;
		for(int j=0; j<31; j++){
			if((number & flag) == 1){
				//說明最右邊一位為1
				System.out.print(1);
			}else{
				System.out.print(0);
			}
			number = number >> 1;
		}
		
		System.out.print("\n");
	}
	
}