コンセプト
ビットマップとはビットマップの略で、いわゆるビットマップとは、ビットごとに一定の状態を格納するもので、大規模なデータに適しており、データは繰り返されない単純なデータです。通常、あるデータが存在するかどうかを判断するために使用されます。
例: ソートされていない 40 億の繰り返しのない unsigned int 整数を指定して数値を指定すると、この数値が 40 億の数値の中にあるかどうかをすばやく判断するにはどうすればよいでしょうか?データの量を見ない場合、私たちの最初の
考え最初から順番にトラバースする必要がありますが、データ量は非常に多く、40 億あり、40 億回トラバースするために消費される時間とメモリは非常に大きくなります。しかし、ビットマップが導入された後、そのような大量のデータ参照が存在するかどうかの問題は明確に解決できます。この数が存在するかどうかを調べる時間の計算量は O(1) であり、32 倍の容量を節約できます (以下で説明)。ビットマップの原理とコードの実装を見てみましょう
原理
数が存在するかどうかを調べるには、実際には存在するかどうかが答えになります. このような、イエスかノーで答えればよい質問は、2 進数のビットで表すことができます. 1 はその数が存在することを意味し、0 は存在することを意味します.番号は存在しません。また、ビットマップの各データ単位はビットです. このように、通常は 32 ビットと 4 バイトでデータを格納する必要がありますが、現在は「データを格納する」ために 1 バイトを費やすだけで済みます. 容量は約 32 削減されます.回。たとえば、40G のデータを保存するには 1.3G しか消費しません。ただし、通常操作する最小のデータ型はバイトです。ビットを直接操作することはできないため、ビット操作を使用してデータを操作できます。データがビットマップに格納される方法を見てみましょう.
ここでは、配列
int arr[] = {1,2,4,5,7,10,11,14,16,17,21,23, 24を与えます, 28, 29, 31}; その場合、これらのデータを格納するために 1 バイトを費やすだけで済みます.
説明: 現在のマシンの多くはリトルエンディアンで格納されています, つまり、下位アドレスは下位ビットに格納されています. 整数データでは,最初のバイトは数字 0 ~ 7 を格納するために使用され、2 番目のバイトは数字 8 ~ 15 を格納するために使用され、3 番目のバイトは数字 16 ~ 23 を格納するために使用され、4 番目のバイトは数字を格納するために使用されます。 24 ~ 31 の数字。数値 10 がどのように格納されるかを見てみましょう。最初に 32 のモジュラスを渡します。残りは 10 のままです。次に、4 バイトの 10 番目のビットを 1 に設定して、数値が表示されたことを示します。私たちのマシンはリトルエンディアンのストレージであるため、下の図に示すように、各ビットは右から計算する必要があるため、
対応するビット位置を 1 に設定するだけで済みます。しかし、保存したいデータが巨大な場合はどうなるでしょうか? 実はこれも非常に単純です. 配列をビットマップとして定義することができます. 数値が 0-31 の場合は添字 0 の要素に格納して操作します. 32-63 の場合は次のようになります.添字間操作No.1になります。下付き文字の計算 モジュロ 32 で下付き文字を取得できます。
ビットマップの原理がわかったら、原理に従ってコードでビットマップを実装しましょう
達成
メンバー変数とコンストラクター: ビットマップの実装では、メンバー変数は実装する配列のみが必要です。そして、この配列はどのくらいの大きさで、どのくらいの大きさで開きたいでしょうか? 配列に追加の整数スペースを開くと、さらに 32 個の数値を格納できるため、ユーザーが正確な数値を提供できるようになります.この数値はデータ量であり、数値の最大範囲です. range/32 + 1
モジュロ 32 で配列のサイズを取得できますが、0~31 モジュロ 32 は 0 です。0 のスペースを開くことは明らかに不適切なので、スペースのサイズの配列を開く必要があります。
データの格納: 数値 num の格納には 3 つの手順が必要です. 1 つ目は、値に対応する配列添字を計算することです. 配列添字を計算する方法は、idx=num / 32 です。2 番目のステップは、対応する整数 bitIdx=num%32 の num のビット位置を計算することです。3 番目のステップは、計算されたビット位置を 1 に設定することです。
前に述べたように、ビットを操作するには、ビット操作を介して操作できます。最初にbitIdxだけ左に 1 をシフトし、次に整数で OR 操作を実行します
。左に 5 ビット ==>100000
2. データと最初のステップで計算された結果の OR
10010011 | 100000 =10110011、次に、指定された位置を 1 に設定します。
データの検索: データが存在するかどうかを判断するには、実際にはデータを格納するのと同じように、idx と bitIdx の 2 つの位置を計算する必要があります。次に、これら2つの位置を使用して、対応する位置が1であるかどうかを判断し、1の場合は番号が存在することを意味します. どのように判断するのですか?最初に idx として添え字が付けられた整数を bitIdx だけ右にシフトし、次に 1 との AND 演算を実行できます. 1 の場合は存在することを意味し、そうでない場合は存在しないことを意味します. たとえば、bitIdx=5 と仮定すると
、 data is 10110011
1. 右シフト 5 ビット 00000101
2. 最初のステップで計算した結果と
1 の AND 演算を実行します。
データの削除: データを削除する操作は、データを格納する操作と同じですが、唯一の違いは、対応するビット位置が 0 に設定されることです。最初に 1 を bitIdx だけ左にシフトし、次に反転し、結果を元のデータと AND します. たとえば、
bitIdx=5 とすると、データは 10110011
1. 1 を 5 ビット左にシフトし、011111
2を反転します. . 最初のステップで計算した結果とデータ
10110011 & 011111 = 10010011 の AND 演算を実行すると、削除は成功します。
コード:
class BitMap
{
public:
//位图的内存大小和数据范围有关
BitMap(size_t range)
:_bit(range / 32 + 1)
{
}
void set(const size_t num)
{
//计算数组中的下标
int idx = num / 32;
//计算num在对应下标整数中的下标位置
int bitIdx = num % 32;
//将对应的比特位置1
_bit[idx] |= 1 << bitIdx;
}
bool find(const size_t num)
{
int idx = num / 32;
int bitIdx = num % 32;
return (_bit[idx] >> bitIdx) & 1;
}
void reset(const size_t num)
{
int idx = num / 32;
int bitIdx = num % 32;
_bit[idx] &= ~(1 << bitIdx);
}
private:
vector<int> _bit;
};
テストのスクリーンショット: