11

Jon Bentley は、著書「プログラミング パール」のコラム 1 で、ビット ベクトルを使用してゼロ以外の正の整数のシーケンスをソートする手法を紹介しています。

ここからプログラム bitsort.c を取得し、以下に貼り付けました。

/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */

/* bitsort.c -- bitmap sort from Column 1
 *   Sort distinct integers in the range [0..N-1]
 */

#include <stdio.h>

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000

int a[1 + N/BITSPERWORD];

void set(int i) 
{
    int sh = i>>SHIFT;
    a[i>>SHIFT] |=  (1<<(i & MASK)); 
}
void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }

int main()
{   int i;
for (i = 0; i < N; i++)
    clr(i);

    /*Replace above 2 lines with below 3 for word-parallel init
    int top = 1 + N/BITSPERWORD;
    for (i = 0; i < top; i++)
    a[i] = 0;
    */

while (scanf("%d", &i) != EOF)
    set(i);
for (i = 0; i < N; i++)
        if (test(i))
    printf("%d\n", i);
return 0;
}

関数 clr、set、および test が何をしているかを理解し、以下で説明します: (ここで間違っている場合は修正してください)。

  • clr は i 番目のビットをクリアします
  • set は i ビットを設定します
  • テストは i 番目のビットの値を返します

さて、関数がどのように機能するのかわかりません。これら 3 つの関数で発生しているすべてのビット操作を把握することはできません。

4

6 に答える 6

23

最初の3つの定数は相互に関連しています。BITSPERWORDは32です。これは、コンパイラーとアーキテクチャーに基づいて設定する必要があります。2 ^ 5 = 32であるため、SHIFTは5です。最後に、MASKは0x1Fで、バイナリでは11111です(つまり、下位5ビットがすべて設定されています)。同様に、MASK=BITSPERWORD-1。

ビットセットは、概念的には単なるビット配列です。この実装は実際にはintの配列を使用し、intあたり32ビットを想定しています。したがって、少し設定、クリア、またはテスト(読み取り)する場合は常に、次の2つのことを理解する必要があります。

  • (配列の)どのintにありますか
  • そのintのどのビットについて話しているのか

intあたり32ビットを想定しているため、32で除算(および切り捨て)して、必要な配列インデックスを取得できます。32で割る(BITSPERWORD)は、右に5で割る(SHIFT)と同じです。これがa[i>>SHIFT]ビットの目的です。これをa[i/ BITSPERWORD]と書くこともできます(実際、コンパイラーに妥当なオプティマイザーがあると仮定すると、おそらく同じまたは非常に類似したコードが得られます)。

必要な要素がわかったので、どのビットを見つける必要があります。本当に、残りが欲しいです。これはi%BITSPERWORDで実行できますが、i&MASKは同等であることがわかります。これは、BITSPERWORDが2の累乗(この場合は2 ^ 5)であり、MASKがすべて設定された下位5ビットであるためです。

于 2009-06-26T17:56:03.817 に答える
4

基本的には、最適化されたバケット ソートです。

  • 長さ n ビットのビット配列を予約します。
  • ビット配列をクリアします (最初はメイン)。
  • 項目を 1 つずつ読み取ります (それらはすべて別個のものでなければなりません)。
    • 読み取り番号が i の場合、ビット配列の i 番目のビットを設定します。
  • ビット配列を繰り返します。
    • ビットが設定されている場合は、位置を出力します。

または言い換えると (N < 10 の場合、3 つの数字 4、6、2 を並べ替える場合) 0

空の 10 ビット配列 (通常は 1 つの整数) で開始します。

0000000000

4 を読み取り、配列内のビットを設定します。

0000100000

6 を読み取り、配列内のビットを設定します

0000101000

2 を読み取り、配列内のビットを設定します

0010101000

配列を反復し、ビットが 1 に設定されているすべての位置を出力します。

2、4、6

並べ替えました。

于 2009-06-26T17:34:44.343 に答える
3

set() から始めます:
5 の右シフトは 32 で除算することと同じです。これは、ビットがどの int にあるかを見つけるために行われます
。MASK は 0x1f または 31 です。アドレスとの AND 演算により、int 内のビット インデックスが得られます。これは、アドレスを 32 で割った余りと同じです。
ビット インデックス ("1<<(i & MASK)") だけ 1 を左にシフトすると、指定された位置セットに 1 ビットだけを持つ整数が得られます。
ORing によってビットが設定されます。
「int sh = i>>SHIFT;」という行 彼らはその下でshを再び使用せず、代わりに「i>>SHIFT」を繰り返しただけなので、無駄な行です。

clr() は基本的に set と同じですが、ビットを設定するために 1<<(i & MASK) と OR する代わりに、逆数と AND してビットをクリアします。test() 1<<(i & MASK) の AND を使用して、ビットをテストします。

ビットソートは、整数ごとに最大 1 つしかカウントしないため、リストから重複も削除します。ビットの代わりに整数を使用してそれぞれを 1 以上カウントするソートは、基数ソートと呼ばれます。

于 2009-06-26T17:41:01.933 に答える
-3

いくつかの疑問 : 1. なぜ 32 ビットが必要なのですか? 2. 0000000 から 9999999 までのキーと、ビットの有無に基づいて値 0 または 1 を持つ HashMap を作成することにより、Java でこれを実行できますか? このようなプログラムにはどのような影響がありますか?

于 2009-09-14T12:01:20.683 に答える