ビットマップの基本的な考え方は、ビットを使用して要素に対応する値をマークすることであり、キーは要素です。ビット単位でデータを格納するため、ストレージ容量を大幅に節約できます。(追記:ストレージスペースの節約に重点を 置きます)
いくつかの基本的な概念を投稿します。
Javaでは8つの基本タイプがバイトを占有します。
整数型:
バイト 1バイト
短い 2バイト
int4 バイト
長い 8バイト
浮動小数点型:
float型 4バイトを
ダブル 8つのバイトを
文字タイプ:
char2 バイト
ブール値:
ブール値 1バイト
1バイトは8ビットに等しい:1バイト= 8ビット
ビット演算:
Javaでは、intデータの最下層は補数の形式で格納されます。int変数は32ビットを使用してデータを格納し、最上位ビットは符号ビット、0は正の数、1は負の数を意味Integer.toBinaryString()
し、ビット文字列に変換できます。
出力:
左にシフト<< 例:5 << 2 = 20
数値がオーバーフローしないことを前提として、正と負の数値の場合、1ビットを左にシフトすることは2の1乗を乗算することと同等であり、nビットを左にシフトすることは2のn乗を乗算することと同等です。 。
たとえば、5をバイナリ表現に変換します:0000 0000 0000 0000 0000 0000 0000 0101
次に、左に2桁シフトした後、下の桁に0を追加します。000000000000 0000 0000 0000 00010100を10進数の20に変換します。
実際、整数で乗算または除算されている限り、shiftメソッドを使用して次のような結果を得ることができます。
a = a * 5
分析a9は、a4 + a1であるa(4 + 1)に分割できるため、次のように変更できます。a=(a << 2)+ a
[注] +/-演算子はシフト演算子よりも優先度が高いため、数式を作成するときはかっこを追加することを忘れないでください。a= a * 12はa = a << 3 + a << 2と同等ではありません。 a =(a << 3)+(a << 2)として記述されます。
右にシフト>> 例:5 >> 2 = 1
数値がオーバーフローしないことを前提として、正と負の数値の場合、1ビットを右にシフトすることは2で除算することと同等であり、nビットを右にシフトすることは2のn乗で除算することと同等です。
または、最初に5をバイナリ表現に変換します:0000 0000 0000 0000 0000 0000 0000 0101
次に、右に2桁シフトし、上位桁に0を追加します。000000000000 0000 0000 0000 0000 0001 10進法に変換すると、1になります(余分な部分は直接切り取られます)。
符号なし右シフト>>>
5 >>> 3
Javaではint型が32ビットを占めることがわかっています。これは、正の数または負の数を表すことができます。2進数に変換された正の数の最上位ビットは0であり、負の数の最上位ビットは1です。2の補数コードの加算演算は通常の計算と同じで、符号ビットも演算に含まれますが、最終的には32ビットしか保持されません。
-5はバイナリに変換されます:1111 1111 1111 1111 1111 1111 1111 1011
-5は右に3ビットシフトされます。1111111111111111 1111 1111 1111 1111 //(1を追加すると、結果は-1になり、正の数の上位ビットは0で埋められます)
-5符号なし右シフト3ビット:0001 1111 1111 1111 1111 1111 1111 1111 //(0を追加すると、結果は536870911になります)
ビットと&
第1オペランドのn番目は第2オペランドのn番目の位置にあります。両方が1の場合、結果のn番目も1になり、それ以外の場合は0になります。
ビットまたは|
第1オペランドのn番目のビットは第2オペランドのn番目のビットにあります。1つが1である限り、1であり、それ以外の場合は0です。
^ XOR
第1オペランドのn番目は、第2オペランドのn番目の位置にあります(値が異なる場合は0と1)、それ以外の場合は0です。
これは、余りを見つけることと同等です。たとえば、47 ^ 32は48%32 = 15と同等です。
ここにポイントがあります:
今、そのような要求があります。20億のランダムな整数の中に特定の数mが存在するかどうかを調べ、32ビットオペレーティングシステム、4Gメモリを想定します。
各番号は、INTに保存されている場合、占有スペースが(2000000000 * / 1024/1024/1024 4)≈程度であるので、それは、20億個のint値である7.45 G
ビット単位のストレージが異なる場合、20億、約占め20億ビット(2000000000/8/1024/1024/1024)≈で0.23 G
これ以上言う必要はありません
それで、問題は、どのように数を表すかということです。
今言ったように、各桁は数字を表し、0はそれが存在しないことを意味し、1はそれが存在することを意味します。これは2進数と一致しています。
このようにして、数字{1,2,4,6}を簡単に表すことができます。
写真を盗む。一人では描けない
コンピュータのメモリ割り当ての最小単位はバイト、つまり8ビットです。{12,13,15}を表したい場合はどうでしょうか。
もちろん別の8ビットで表現されます
int番号のどの添え字がtmp配列にあるかを判別する方法。これは、実際には32で直接除算して整数部分を取得することで取得できます。たとえば、整数8を32で除算すると、0に丸められ、8はtmp上にあります。 [0]。さらに、8の32ビットのどれがtmp [0]にあるかをどのように知ることができますか?この場合、直接mod 32は問題なく、整数8と同様に、32はtmp [0] 8の8番目のmodに等しくなります。の場合、整数8はtmp [0]の8番目のビットにあります(右から数えて)。
ビットマップソースコードを貼り付けます
プライベートロングレングス;
private static int [] bitsMap;
プライベート静的最終INT [] BIT_VALUE = {0x00000001に、0x00000002、0x00000004、0x00000008、0x00000010、0x00000020、
0x00000040、0x00000080、0x00000100、0x00000200、0x00000400、0x00000800、0x00001000、0x00002000、0x00004000、
0x00008000、0x00010000、0x00020000、0x00040000、0x00080000、0x00100000 、0x00200000、0x00400000、0x00800000、0x01000000、0x02000000、0x04000000、0x08000000、0x10000000、0x20000000、0x40000000、0x80000000
};
public BitMap2(long length){ this.length = length; / ** *長さに従って計算し、必要な配列サイズ * length%32 = 0の場合、サイズは次のようになります * = length / 32 * length%32の場合> 0、サイズは次の値に等しい * = length / 32 + l * / bitsMap = new int [(int)(length >> 5)+((length&31)> 0?1:0)]; }
/ **
* @paramn設定する値はnです
* /
public void setN(long n){ if(n <0 || n> length){ throw new IllegalArgumentException( "length value" + n + "は不正です! "); } //ビットマップの添え字を検索します。nは" n / 5 "に相当します。intindex =(int)n >> 5; //値(残り)のオフセットを検索します。" nに相当します。 %31 " int offset =(int)n&31; / ** * * int bits = bitsMap [index]; * bitsMap [index] = bits | BIT_VALUE [offset]; *と同等です。たとえば、n = 3の場合、バイトの4番目の位置を1に設定します(0から数えて、bitsMap [0]で表される数値は0〜31で、左から右への各ビットは数値を表します)
* bitsMap [0] = 00000000 00000000 00000000 00000000 | 00000000 00000000 00000000 00001000 = 00000000 00000000 00000000 00000000 00001000
*つまり:bitsMap [0] = 0 | 0x00000008 = 3
*
*たとえば、n = 4の場合、の5番目の位置を設定します。 1としてのバイト
* bitsMap [0] = 00000000 00000000 00000000 00001000 | 00000000 00000000 00000000 00010000 = 00000000 00000000 00000000 00000000
00011000 *つまり:bitsMap [0] = 3 | 0x00000010 = 12
* /
bitsMap [index] | = BIT_VALUE [offset] ;
}
/
***获取值N是否存在
* @ return 1:存在、0:不存在
* /
public int isExist(long n){ if(n <0 || n> length){ throw new IllegalArgumentException( "length value違法! "); } int index =(int)n >> 5; intオフセット=(int)n&31; intビット=(int)bitsMap [index]; // System.out.println( "n =" + n + "、index =" + index + "、offset =" + offset + "、bits =" + Integer.toBinaryString(bitsMap [index])); return((bits&BIT_VALUE [offset]))>>>オフセット; }
追加
ここに質問があります、どうやってそれに数字を入れるのですか?たとえば、5を入れたい場合、どのように行いますか?
まず、5/32 = 0、5%32 = 5、つまりtmp [0]の5番目の位置にある必要があります。次に、1を5桁左に移動し、次にビット単位のORを実行します。
バイナリへは
これは86 | 32 = 118に相当します
86 | (1 << 5)= 118
b [0] = b [0] | (1 << 5)
つまり、数値を挿入する場合は、数値を表す数値と一緒に1を左に移動してから、元の数値とビット単位のOR演算を実行します。
簡単に言うと、86 +(5/8)|(1 <<(5%8))
したがって、式は次のように要約できます。p+(i / 8)|(1 <<(i%8))ここで、pは現在の値を表し、iは挿入される数を表します。
晴れ
上記は追加ですが、クリアしたい場合はどうすればよいですか?
または、上記の例で、6を削除したいとします。どうすればよいですか?
写真から、数字の位置だけが0です
1を6桁左にシフトして、数字6で表される数字に到達し、次にビットで反転し、最後に元の数字をビット単位のANDで反転して、位置が0になるようにします。
b [0] = b [0]&(〜(1 << 6))
b [0] = b [0]&(〜(1 <<(i%8)))、
上記のコード:
public class BitMap { // private byte [] bits; //格納できるデータ 量 privateintcapacity ; public BitMap(intcapacity ){ this.capacity = capacity; // 1bitは8データを格納でき、次に容量データいくつビットが必要です。容量/ 8 + 1、3ビットで右シフトすることは8 ビットで割ることと同じです=新しいバイト[(容量>> 3)+1]; } public void add(int num){ // num / 8 byte []のインデックスを取得します intarrayIndex = num >> 3; // num%8はbyte [index]の位置を取得しますintposition = num&0x07; // 1を左の位置にシフトした後、その位置は当然1になります、次にDo |を前のデータで実行して、位置を1に置き換えます。 bits [arrayIndex] | = 1 << position; } public boolean contains (int num){
// num / 8はbyte []のインデックスを取得します
intarrayIndex = num >> 3;
// num%8はbyte [index]の位置を取得しますintposition
= num&0x07;
// 1を左の位置にシフトした後、その位置当然1です。次に、前のデータで&を実行して、0かどうかを判別し
ます。Return(bits [arrayIndex]&(1 << position))!= 0;
}
public void clear(int num){ / / num / 8 byte []のインデックスを取得します intarrayIndex = num >> 3; // num%8はbyte [index]の位置を取得しますintposition = num&0x07; // 1を左の位置にシフトした後、位置は当然1です。反対の方向に進み、現在の値で&を実行して、現在の位置をクリアし ます。bits[arrayIndex]&=〜(1 << position);
}
public static void main(String [] args){ BitMapビットマップ= new BitMap(100);ビットマップ .add(7); System.out.println( "Insert 7 success"); boolean isexsit =ビットマップ.contain(7) ; System.out.println( "7が存在します:" + isexsit); ビットマップ.clear(7); isexsit =ビットマップ.contain(7); System.out.println( "7が存在します:" + isexsit); } }
検索
また、各桁は数値を表し、1は存在する(または存在する)ことを意味し、0は存在しない(または存在しない)ことを意味します。値を1または0に設定してガイの加算と削除を行うことにより、数値が存在するかどうかを判断することは、数値のビットが0または1であるかどうかを判断することです。
3の存在を知りたい場合、b [0]&(1 << 3)を判断するだけで済みます。この値が0の場合は存在せず、1の場合は存在することを意味します。
アプリケーションシナリオ:
1:小さなシーンを見る> 3億個の整数の中から繰り返されない整数を見つけ、3億個の整数を保持するにはメモリが不十分になるように制限します。
この種のシナリオでは、2ビットマップを使用して解決できます。つまり、整数ごとに2ビットを割り当て、0と1のさまざまな組み合わせを使用して、特別な意味を識別できます。たとえば、00は整数が表示されていないことを意味します。 、01は1回、11は表示されることを意味します。何度も繰り返すと、繰り返される整数を見つけることができます。必要なメモリスペースは、通常のビットマップの2倍、つまり3億* 2/8/1024/1024 = 71.5MBです。 。
具体的なプロセスは次のとおりです。
3億の整数をスキャンし、ビットマップをグループ化し、最初にビットマップ内の対応する位置を確認します。00が01の場合、01が11の場合、11の場合、変更されません。3億の整数をスキャンした後、ビットマップ全体が組み立てられました。最後に、ビットマップを確認し、ビット11に対応する整数を出力します。
例:数値0はインデックス[0]と[1]の位置を占めます
2:あるファイルにはいくつかの電話番号が含まれており、各番号は8桁であり、異なる番号の数がカウントされることがわかっています。
8ビットから99999999まで、約99mビット、約10mバイトのメモリで十分です。(0〜99 999 999の数値として理解でき、各数値はビットビットに対応するため、99Mビット== 1.2Mバイトのみが必要です。このように、小さな1.2Mメモリを使用して8桁すべてを表します。電話)