web-dev-qa-db-ja.com

Cでビットの配列を定義して操作するにはどうすればよいですか?

「0」と「1」を書き込む非常に大きな配列を作成したい。私は、長さ2のユニット、ダイマーが、互いに重なり合うことなく、ランダムな位置のn次元格子上に堆積される、ランダムシーケンシャル吸着と呼ばれる物理プロセスをシミュレートしようとしています。より多くの二量体を堆積させるための格子上の余地がなくなると、プロセスは停止します(格子が詰まります)。

最初はゼロの格子から始め、二量体は「1」のペアで表されます。各二量体が堆積すると、二量体が重なり合わないという事実により、二量体の左側の部位がブロックされる。そこで、ラティス上にトリプルの「1」を配置することにより、このプロセスをシミュレートします。シミュレーション全体を何度も繰り返してから、平均カバレッジ%を算出する必要があります。

1Dラティスと2Dラティスの文字の配列を使用して、すでにこれを行っています。現時点では、3D問題とより複雑な一般化に取り組む前に、コードをできるだけ効率的にしようとしています。

これは基本的に、コードが1Dで単純化されて見えるものです。

int main()
{
    /* Define lattice */
    array = (char*)malloc(N * sizeof(char));

    total_c = 0;

    /* Carry out RSA multiple times */
    for (i = 0; i < 1000; i++)
        Rand_seq_ads();

    /* Calculate average coverage efficiency at jamming */
    printf("coverage efficiency = %lf", total_c/1000);

    return 0;
}

void Rand_seq_ads()
{
    /* Initialise array, initial conditions */
    memset(a, 0, N * sizeof(char));
    available_sites = N;
    count = 0;

    /* While the lattice still has enough room... */
    while(available_sites != 0)
    {
        /* Generate random site location */
        x = Rand();

        /* Deposit dimer (if site is available) */
        if(array[x] == 0)
        {
            array[x] = 1;
            array[x+1] = 1;
            count += 1;
            available_sites += -2;
        }

        /* Mark site left of dimer as unavailable (if its empty) */
        if(array[x-1] == 0)
        {
            array[x-1] = 1;
            available_sites += -1;
        }
    }

    /* Calculate coverage %, and add to total */
    c = count/N
    total_c += c;
}

私がやっている実際のプロジェクトでは、ダイマーだけでなく、トリマー、クアドリマー、およびあらゆる種類の形状とサイズ(2Dおよび3D)が関係しています。

私はバイトではなく個々のビットで作業できることを望んでいましたが、私は読んでいて、一度に1バイトしか変更できないと言うことができる限り、複雑なインデックス付けを行う必要がありますまたはそれを行う簡単な方法はありますか?

答えてくれてありがとう

39
Eddy

手遅れではない場合は、 this ページに例を使用して素晴らしい説明があります。

intの配列を使用して、bitsの配列を処理できます。 intのサイズを4 bytesとすると、intについて話すときは、32 bitsを扱います。 int A[10]があるとします。つまり、10*4*8 = 320 bitsに取り組んでおり、次の図に示すとおりです(配列の各要素には4つの大きなブロックがあり、それぞれがbyteとそれぞれ小さいブロックはbitを表します)

enter image description here

したがって、配列kAthビットを設定するには:

void  SetBit( int A[],  int k )
   {
      int i = k/32;        //gives the corresponding index in the array A
      int pos = k%32;      //gives the corresponding bit position in A[i]

      unsigned int flag = 1;   // flag = 0000.....00001

      flag = flag << pos;      // flag = 0000...010...000   (shifted k positions)

      A[i] = A[i] | flag;      // Set the bit at the k-th position in A[i]
   }

または短縮版で

void  SetBit( int A[],  int k )
   {
      A[k/32] |= 1 << (k%32);  // Set the bit at the k-th position in A[i]
   }

kthビットをクリアするのと同様:

void  ClearBit( int A[],  int k )                
   {
      A[k/32] &= ~(1 << (k%32));
   }

kthビットがテストされます:

int TestBit( int A[],  int k )
   {
      return ( (A[k/32] & (1 << (k%32) )) != 0 ) ;     
   }

上記のように、これらの操作はマクロとしても記述できます。

#define SetBit(A,k)     ( A[(k/32)] |= (1 << (k%32)) )
#define ClearBit(A,k)   ( A[(k/32)] &= ~(1 << (k%32)) )            
#define TestBit(A,k)    ( A[(k/32)] & (1 << (k%32)) )
43
aniliitb10
typedef unsigned long bfield_t[ size_needed/sizeof(long) ];
// long because that's probably what your cpu is best at
// The size_needed should be evenly divisable by sizeof(long) or
// you could (sizeof(long)-1+size_needed)/sizeof(long) to force it to round up

現在、bfield_tの各longは、sizeof(long)* 8ビットを保持できます。

必要なビッグのインデックスは次の方法で計算できます。

bindex = index / (8 * sizeof(long) );

そしてあなたのビット数

b = index % (8 * sizeof(long) );

その後、必要な長さを調べて、必要なビットをマスクします。

result = my_field[bindex] & (1<<b);

または

result = 1 & (my_field[bindex]>>b); // if you prefer them to be in bit0

最初の方法は、一部のCPUでより高速であるか、複数ビット配列の同じビット間で操作を実行する必要があるため、後ろにシフトする必要がありません。また、2番目の実装よりも、フィールドのビットの設定とクリアをより厳密に反映します。セット:

my_field[bindex] |= 1<<b;

明確:

my_field[bindex] &= ~(1<<b);

フィールドを保持するlongでビット単位の演算を使用でき、それが個々のビットの演算と同じであることを覚えておく必要があります。

可能であれば、おそらくffs、fls、ffc、およびflc関数も調べてください。 ffsは常にstrings.h。この目的のためだけにあります-ビットの文字列。とにかく、それは最初のセットを見つけ、本質的に:

int ffs(int x) {
    int c = 0;
    while (!(x&1) ) {
        c++;
        x>>=1;
    }
    return c; // except that it handles x = 0 differently
}

これは、プロセッサが命令を持っている一般的な操作であり、コンパイラはおそらく、私が書いたような関数を呼び出すのではなく、その命令を生成します。ちなみに、x86にはこのための命令があります。ああ、ffslとffsllは、それぞれtake longとlong longを除いて同じ関数です。

9
nategoose

&(ビットごとのand)と<<(左シフト)を使用できます。

たとえば、(1 << 3)はバイナリで "00001000"になります。したがって、コードは次のようになります。

char eightBits = 0;

//Set the 5th and 6th bits from the right to 1
eightBits &= (1 << 4);
eightBits &= (1 << 5);
//eightBits now looks like "00110000". 

次に、charsの配列でスケールアップし、最初に変更する適切なバイトを見つけます。

より効率的にするために、事前にビットフィールドのリストを定義し、それらを配列に入れることができます。

#define BIT8 0x01
#define BIT7 0x02
#define BIT6 0x04
#define BIT5 0x08
#define BIT4 0x10
#define BIT3 0x20
#define BIT2 0x40
#define BIT1 0x80

char bits[8] = {BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, BIT8};

その後、ビットシフトのオーバーヘッドを回避し、ビットにインデックスを付けて、前のコードを次のように変換できます。

eightBits &= (bits[3] & bits[4]);

あるいは、C++を使用できる場合は、std::vector<bool>これは、ビットのベクトルとして内部的に定義されており、直接インデックス付けが完了しています。

6
David

bitarray.h

_#include <inttypes.h> // defines uint32_t

//typedef unsigned int bitarray_t; // if you know that int is 32 bits
typedef uint32_t bitarray_t;

#define RESERVE_BITS(n) (((n)+0x1f)>>5)
#define DW_INDEX(x) ((x)>>5)
#define BIT_INDEX(x) ((x)&0x1f)
#define getbit(array,index) (((array)[DW_INDEX(index)]>>BIT_INDEX(index))&1)
#define putbit(array, index, bit) \
    ((bit)&1 ?  ((array)[DW_INDEX(index)] |= 1<<BIT_INDEX(index)) \
             :  ((array)[DW_INDEX(index)] &= ~(1<<BIT_INDEX(index))) \
             , 0 \
    )
_

つかいます:

_bitarray_t arr[RESERVE_BITS(130)] = {0, 0x12345678,0xabcdef0,0xffff0000,0};
int i = getbit(arr,5);
putbit(arr,6,1);
int x=2;            // the least significant bit is 0
putbit(arr,6,x);    // sets bit 6 to 0 because 2&1 is 0
putbit(arr,6,!!x);  // sets bit 6 to 1 because !!2 is 1
_

編集ドキュメント:

"dword" = "double Word" = 32ビット値(符号なし、しかし実際には重要ではない)

_RESERVE_BITS: number_of_bits --> number_of_dwords
    RESERVE_BITS(n) is the number of 32-bit integers enough to store n bits
DW_INDEX: bit_index_in_array --> dword_index_in_array
    DW_INDEX(i) is the index of dword where the i-th bit is stored.
    Both bit and dword indexes start from 0.
BIT_INDEX: bit_index_in_array --> bit_index_in_dword
    If i is the number of some bit in the array, BIT_INDEX(i) is the number
    of that bit in the dword where the bit is stored.
    And the dword is known via DW_INDEX().
getbit: bit_array, bit_index_in_array --> bit_value
putbit: bit_array, bit_index_in_array, bit_value --> 0
_

getbit(array,i)は、ビットiを含むdwordをフェッチし、shiftsdwordrightを取得するため、ビットiは最下位ビット。次に、bitwise andwith 1は、他のすべてのビットをクリアします。

putbit(array, i, v)最初にvの最下位ビットをチェックします。 0の場合、ビットをクリアする必要があり、1の場合、設定する必要があります。
ビットを設定するには、ビットを含むdwordのビットまたはを行い、1の値を左にシフトしますby bit_index_in_dword:そのビットは設定され、他のビットは変更されません。
ビットをクリアするには、ビットを含むdwordのビットごとのandビットごとの補数 of 1左にシフトbit_index_in_dword:その値には、クリアしたい位置にあるゼロビット以外のすべてのビットが1に設定されています。
マクロは_, 0_で終了します。そうしないと、ビットiが格納されているdwordの値が返され、その値には意味がありません。 _((void)0)_を使用することもできます。

それはトレードオフです:

(1)各2ビット値に1バイトを使用-シンプルで高速、ただし4倍のメモリを使用

(2)ビットをバイトにパック-より複雑で、いくらかのパフォーマンスオーバーヘッドがあり、最小限のメモリを使用します

十分なメモリがある場合は(1)に進み、そうでない場合は(2)を検討してください。

2
Paul R