この質問と回答を確認して、次のアイデアを思いつきました。
int i = n-1;
uint32_t y = x;
while(y && i--) {
y = y & (y << 1);
};
連続するセット ビットy
がある場合、上記の操作の後は非ゼロになります。n
次に行うことは、そこに設定されている最も重要でない値を見つけることです。次のコードは、最下位ビットを除くすべての設定ビットを取り除きます。
z = y - (y & (y-1));
ビットセットが 1 つしかないので、ビットの位置を見つける必要があります。32 ケースの switch ステートメントを使用できます。
static inline int get_set_position(const uint32_t z) {
switch(z) {
case 0x1:
return 0;
case 0x2:
return 1;
....
.... // upto (1<<31) total 32 times.
}
return -1;
}
最終的に結果を得るには、 を減らす必要がありますn-1
。したがって、全体の手順は次のとおりです。
static inline int get_set_n_position(const uint32_t x, const uint8_t n) {
if(!n) return -1;
int i = n-1;
uint32_t y = x;
while(y && i--) {
y = y & (y << 1);
};
if(!y) return -1;
uint32_t z = y - (y & (y-1));
if(!z) return -1;
int pos = get_set_position(z);
if(pos < 0) return -1;
assert(pos >= (n-1));
return pos - (n-1);
}
現在、ビッグエンディアンが懸念されています。ビッグエンディアンの get_set_position() を変更して機能させるだけでよいと思います(連続したセットビットの定義がエンディアンネスに基づいて変更されると仮定します)。
gcc が提供する builtin_ctzl を使用するテスト済みのコードを共有させてください。
OPP_INLINE int get_set_n_position(BITSTRING_TYPE x, const uint8_t n) {
if(!n || n > BIT_PER_STRING) return -1;
int i = n-1;
while(x && i--) {
x = x & (x << 1);
};
if(!x) return -1;
int pos = __builtin_ctzl(x);
return pos - (n-1);
}
32 は定数であるため (@Qnan が気づいたように)、コードは O(1) 時間で動作します。ここでも、レジスタのサイズが異なる場合、O(n) で機能します。
注: コメントと単体テストのおかげで、バグを修正しました。